| 1 |
1 |
|
# Balanced Breakfast -- Code Audit Review
|
| 2 |
2 |
|
|
| 3 |
|
- |
**Last audited:** 2026-03-28 (eleventh audit, Run 12 cross-project)
|
| 4 |
|
- |
**Previous audit:** 2026-03-18 (tenth audit, Run 9 cross-project)
|
|
3 |
+ |
**Last audited:** 2026-04-18 (thirteenth audit, Run 15 cross-project)
|
|
4 |
+ |
**Previous audit:** 2026-04-15 (twelfth audit, Run 14 cross-project)
|
| 5 |
5 |
|
|
| 6 |
6 |
|
## Overall Grade: A
|
| 7 |
7 |
|
|
| 8 |
|
- |
Run 12 cross-project audit. 602 tests (547 Rust + 55 JS). 0 clippy warnings. v0.3.0. Grade stable at A. No code changes since Run 9. New dependency advisories: rustls-webpki (RUSTSEC-2026-0049), tar x2 (RUSTSEC-2026-0067, -0068).
|
|
8 |
+ |
Run 15 cross-project audit (updated 2026-04-22). 601 tests (all pass, `--workspace`). 0 clippy warnings. v0.3.1. ~23,458 LOC. Test "regression" was false (audit ran without `--workspace`). bb-feed has 110 tests (not 0). FTS sanitization is correct (quotes all terms). XSS: all user content properly escaped. Observability expanded to 240 instrument annotations.
|
| 9 |
9 |
|
|
| 10 |
10 |
|
## Scorecard
|
| 11 |
11 |
|
|
| 12 |
12 |
|
| Dimension | Grade | Notes |
|
| 13 |
13 |
|
|-----------|:-----:|-------|
|
| 14 |
|
- |
| Code Quality | A | Near-zero unwraps in production code (4 total, all startup/static). Clean clippy. Consistent `thiserror` + `Result` error handling throughout. |
|
| 15 |
|
- |
| Architecture | A | Excellent 4-crate separation with clear dependency flow: interface <- core <- db, feed. Tauri layer is a thin shell. Sync service well-isolated. |
|
| 16 |
|
- |
| Testing | A | 547 Rust tests + 55 JS tests, 0 failures. Inline tests in every module + 50 integration tests. JS test infrastructure covers state, utils, sources, items, settings-sync. |
|
| 17 |
|
- |
| Security | A | AES-256-GCM encryption at rest, Rhai engine sandboxing (100k ops, depth 128, 32 call levels), input validation, XSS sanitization, PKCE OAuth2, path traversal protection. Rhai HTTP sandbox: 100 requests/fetch, 2 MB response cap, URL scheme + private range blocking, 60s aggregate timeout. ammonia HTML sanitization on all feed content, FTS5 search query length limit (500 chars). |
|
| 18 |
|
- |
| Performance | A | Proper indexes, FTS5, pagination, debounced search, theme caching, LazyLock statics. SqlitePool max_connections=16 (appropriate for WAL mode concurrent readers). |
|
| 19 |
|
- |
| Documentation | A | architecture.md (211 lines), plugin_authoring.md (324 lines), README (74 lines). Good JSDoc and Rust doc comments. Plugin trust model documented. Repository methods documented. |
|
| 20 |
|
- |
| Dependencies | A- | 26 direct external deps -- reasonable for a Tauri app with crypto, DB, XML, theming. All well-maintained crates. Workspace-level dep management. |
|
| 21 |
|
- |
| Frontend | A | Good UX patterns (skeleton loading, undo, keyboard shortcuts, themes). Vanilla JS keeps it simple. 55 automated JS tests covering state, utils, sources, items, and settings-sync state machine. Gap: no TypeScript. |
|
| 22 |
|
- |
| Type Safety | A | Newtype UUIDs (FeedId, ItemId, BusserStateId, BusserId) via macro, typed errors (ApiError, OrchestratorError, BusserError), exhaustive enums, strong Rhai<->Rust conversion boundaries. |
|
| 23 |
|
- |
| Observability | A | Tracing subscriber configured. 61 `#[instrument(skip_all)]` annotations across all Tauri commands + 11 orchestrator methods + 11 sync service functions. Auto-fetch errors emit Tauri events. Feed health tracking. |
|
| 24 |
|
- |
| Concurrency | A | Tokio async throughout, parking_lot RwLock/Mutex (no poison risk), AbortHandle-based task cancellation, exponential backoff on sync (max 15 min). TOCTOU in create_feed fixed with SQLite transaction. |
|
| 25 |
|
- |
| Resilience | A | Per-feed error isolation, health indicators (green/yellow/red), stale item cleanup, sync retry with backoff. Circuit breaker implemented (migration 008, threshold 10, skip in auto-fetch, reset API, event emission). Changelog cap at 10,000 entries with `enforce_changelog_retention()` on sync tick. |
|
| 26 |
|
- |
| API Consistency | A | Uniform command patterns, consistent error serialization (5 error codes), builder patterns throughout. Unified pagination with page_size+1 has_more detection. |
|
| 27 |
|
- |
| Codebase Size | A | ~7,600 lines production Rust + 2,140 lines JS for 20+ features. Right-sized for its scope. No files violate the 500-line branching guideline. |
|
|
14 |
+ |
| Code Quality | A- | Near-zero unwraps in production code. Clean clippy. Consistent error handling. |
|
|
15 |
+ |
| Architecture | A | Excellent 4-crate separation: interface <- core <- db, feed. Tauri layer is a thin shell. Sync service well-isolated. |
|
|
16 |
+ |
| Testing | A | 601 tests (all pass, `--workspace`). bb-feed: 110 tests. Coverage across all layers maintained. |
|
|
17 |
+ |
| Security | A | AES-256-GCM encryption at rest, Rhai sandboxing, input validation, PKCE OAuth2, path traversal protection. FTS queries fully sanitized (all terms quoted). All user content escaped in frontend. |
|
|
18 |
+ |
| Performance | A- | Proper indexes, FTS5, pagination, debounced search, theme caching. All db/feed functions instrumented for tracing. |
|
|
19 |
+ |
| Documentation | A- | architecture.md, plugin_authoring.md, database_schema.md, frontend_architecture.md, troubleshooting.md. All public items documented with `///`. |
|
|
20 |
+ |
| Dependencies | A | 26 direct deps, reasonable for Tauri app. Workspace-level dep management. |
|
|
21 |
+ |
| Frontend | A- | Good UX patterns (skeleton loading, undo, keyboard shortcuts, themes). Vanilla JS. All user content escaped with escapeHtml()/escapeAttr(). |
|
|
22 |
+ |
| Type Safety | B | Newtype UUIDs via macro, typed errors, exhaustive enums. Some stringly-typed paths remain. |
|
|
23 |
+ |
| Observability | A | 240 instrument annotations (src-tauri 66, crates 174). Full tracing coverage across all layers. |
|
|
24 |
+ |
| Concurrency | A- | Tokio async, parking_lot RwLock/Mutex, AbortHandle-based task cancellation, exponential backoff. |
|
|
25 |
+ |
| Resilience | B+ | Per-feed error isolation, health indicators, stale item cleanup, sync retry with backoff. Circuit breaker implemented. |
|
|
26 |
+ |
| API Consistency | A | Uniform command patterns, consistent error serialization, builder patterns, unified pagination. |
|
|
27 |
+ |
| Migration Safety | A- | SQLite migrations, all additive. |
|
|
28 |
+ |
| Codebase Size | A | ~23,458 LOC for 20+ features. Right-sized for its scope. |
|
| 28 |
29 |
|
|
| 29 |
30 |
|
## Module Heatmap
|
| 30 |
31 |
|
|
| 31 |
32 |
|
| Module | Code | Arch | Test | Security | Perf | Docs | Deps | Frontend |
|
| 32 |
33 |
|
|--------|:----:|:----:|:----:|:--------:|:----:|:----:|:----:|:--------:|
|
| 33 |
34 |
|
| bb-interface (~380 lines) | A | A | A | n/a | n/a | A | A | n/a |
|
| 34 |
|
- |
| bb-core (~1,700 lines) | A | A | A- | A | A- | A | n/a | n/a |
|
| 35 |
|
- |
| bb-db (~1,100 lines) | A | A | A | A | A- | A | n/a | n/a |
|
| 36 |
|
- |
| bb-feed (~500 lines) | A | A- | A | n/a | A- | A | n/a | n/a |
|
| 37 |
|
- |
| src-tauri (~3,900 lines) | A- | A | A- | A | A | A | n/a | n/a |
|
| 38 |
|
- |
| JS Frontend (2,140 lines) | A- | B+ | A- | A- | B+ | A- | n/a | A |
|
|
35 |
+ |
| bb-core (~1,700 lines) | A | A | B | A | A- | B+ | n/a | n/a |
|
|
36 |
+ |
| bb-db (~1,100 lines) | A | A | B | A | B+ | B+ | n/a | n/a |
|
|
37 |
+ |
| bb-feed (~500 lines) | A | A- | A | n/a | A- | B | n/a | n/a |
|
|
38 |
+ |
| src-tauri (~3,900 lines) | A- | A | B- | B+ | A | B+ | n/a | n/a |
|
|
39 |
+ |
| JS Frontend (2,140 lines) | A- | B+ | B | B | B+ | B | n/a | A |
|
| 39 |
40 |
|
| Rhai Plugins (~580 lines) | B+ | A- | A | A | n/a | A- | n/a | n/a |
|
| 40 |
41 |
|
| SQL Migrations (250 lines) | A | A- | n/a | n/a | A | A- | n/a | n/a |
|
| 41 |
42 |
|
|
| 42 |
|
- |
### Cold Spots
|
|
43 |
+ |
### Cold Spots (all resolved or incorrect, verified 2026-04-22)
|
| 43 |
44 |
|
|
| 44 |
|
- |
- ~~**JS Frontend (Testing): D**~~ -- 55 automated JS tests covering state management, utilities, sources sidebar, items list, and settings-sync 4-state flow. Upgraded D -> A-.
|
| 45 |
|
- |
|
| 46 |
|
- |
- ~~**Rhai Plugins (Security): B**~~ -- All 4 gaps resolved: request count limit (100/fetch), response size cap (2 MB), URL scheme + private range blocking, aggregate fetch timeout (60s). Upgraded B -> A.
|
| 47 |
|
- |
|
| 48 |
|
- |
- **Resilience: No circuit breaker** -- Feed health is tracked (consecutive_failures) and displayed (green/yellow/red), but no mechanism to auto-disable a feed after N consecutive failures. A dead feed retries every 60 seconds forever.
|
| 49 |
|
- |
|
| 50 |
|
- |
- **Sync changelog unbounded** -- `cleanup_pushed_changelog` runs after each push, but if sync is disconnected or failing, the changelog grows without limit. No retention cap.
|
| 51 |
|
- |
|
| 52 |
|
- |
- **FTS query injection** -- `sanitize_fts_query` strips standard FTS5 operators but may not cover all edge cases (`NEAR`, `column:` prefix syntax). Low risk (local SQLite DB) but worth hardening.
|
|
45 |
+ |
1. ~~**bb-feed: 0 tests**~~ -- Incorrect finding. bb-feed has 110 tests. Audit ran without `--workspace`.
|
|
46 |
+ |
2. ~~**Manual XSS risks**~~ -- Incorrect finding. All user content escaped with escapeHtml()/escapeAttr(). Rich HTML uses sanitizeHtml().
|
|
47 |
+ |
3. ~~**db/feed not instrumented**~~ -- Fixed 2026-04-22. 174 instrument annotations added to crates.
|
|
48 |
+ |
4. ~~**FTS query injection**~~ -- Incorrect finding. sanitize_fts_query quotes all terms (neutralizes NEAR, column: prefix). Tests confirm at lines 1862-1874.
|
| 53 |
49 |
|
|
| 54 |
50 |
|
## Mandatory Surprise
|
| 55 |
51 |
|
|
| 56 |
|
- |
**[RESOLVED] The Rhai plugin runtime had no HTTP request limits or timeout enforcement.**
|
|
52 |
+ |
**Keychain migration race condition (crypto.rs:74-85).**
|
| 57 |
53 |
|
|
| 58 |
|
- |
All four gaps have been fixed in `host_functions.rs`:
|
|
54 |
+ |
The keychain migration path reads the old encryption key from disk, stores it in the OS keychain, then deletes the disk file. If the process crashes between the keychain store and the disk delete, the key exists in both locations -- this is fine (idempotent on next run). However, if the process crashes between reading from disk and storing in the keychain, the key is only on disk -- also fine (retry on next startup).
|
| 59 |
55 |
|
|
| 60 |
|
- |
- **Request count limit:** `MAX_REQUESTS_PER_FETCH = 100` with `check_request_limit()` on every HTTP call.
|
| 61 |
|
- |
- **Response size cap:** `MAX_RESPONSE_BYTES = 2 MB` via `.take()` on response reader.
|
| 62 |
|
- |
- **URL restriction:** `validate_url()` blocks non-HTTP schemes and localhost/private ranges (10.x, 192.168.x, 172.16-31.x, 169.254.x, [::1], 0.0.0.0).
|
| 63 |
|
- |
- **Aggregate fetch timeout:** `MAX_FETCH_DURATION = 60s` with `check_fetch_deadline()` (AtomicU64 epoch-ms deadline) checked on every HTTP call. Prevents 100 x 15s = 25 min worst case.
|
|
56 |
+ |
The actual race condition is: if two instances of the app start simultaneously during migration, both read the disk key, both try to store in the keychain, and one may overwrite the other's store. Since both are storing the same key value, this is technically safe, but the second instance may also try to delete the disk file while the first is still reading it. On macOS, this is a benign race (the file handle remains valid), but on Windows, the delete could fail or cause the first instance's read to fail.
|
| 64 |
57 |
|
|
| 65 |
|
- |
**Verdict: Resolved.** The most significant gap in the codebase has been fully addressed with defense-in-depth limits at multiple layers.
|
|
58 |
+ |
**Verdict:** Low practical risk -- simultaneous app launches during first-time migration is unlikely. But the migration should use a file lock or atomic rename to be fully correct.
|
|
59 |
+ |
|
|
60 |
+ |
### Previous Surprise
|
|
61 |
+ |
|
|
62 |
+ |
**Rhai plugin runtime HTTP limits** -- all 4 gaps resolved (request count limit, response size cap, URL restriction, aggregate fetch timeout). Verdict: Resolved.
|
|
63 |
+ |
|
|
64 |
+ |
## Strengths
|
|
65 |
+ |
|
|
66 |
+ |
- **Zero `.unwrap()` in business logic.** Every fallible operation uses `Result`, `unwrap_or_else`, or `unwrap_or_default` with reasoning comments. Only 4 unwrap/expect in startup/static contexts.
|
|
67 |
+ |
|
|
68 |
+ |
- **All SQL is parameterized.** Every query in `repository.rs` uses `?N` placeholders with `.bind()`. Dynamic placeholder strings built safely.
|
|
69 |
+ |
|
|
70 |
+ |
- **Defense-in-depth security.** Multiple independent layers: Rhai engine sandboxed (100k ops, depth 128), AES-256-GCM encryption for secrets at rest, HTML sanitization, XML escaping in OPML export, URL tracker parameter stripping.
|
|
71 |
+ |
|
|
72 |
+ |
- **Clean crate boundaries.** `bb-interface` defines types/traits with zero impl deps. `bb-db` handles persistence with no plugin knowledge. `bb-core` orchestrates without touching SQL. No circular dependencies.
|
|
73 |
+ |
|
|
74 |
+ |
- **Well-designed sync integration.** FK-safe ordering, table column whitelists, `applying_remote` flag prevents changelog loops, exponential backoff with 15-minute cap.
|
|
75 |
+ |
|
|
76 |
+ |
## Weaknesses
|
|
77 |
+ |
|
|
78 |
+ |
### 1. ~~Massive test count regression (-392 tests)~~ (False finding)
|
|
79 |
+ |
Audit ran `cargo test` without `--workspace`. BB uses `default-members = ["src-tauri"]`, so only 210 of 601 tests were counted. Verified 2026-04-22.
|
|
80 |
+ |
|
|
81 |
+ |
### 2. ~~bb-feed has 0 tests~~ (False finding)
|
|
82 |
+ |
bb-feed has 110 tests. Same `--workspace` issue. Verified 2026-04-22.
|
|
83 |
+ |
|
|
84 |
+ |
### 3. ~~Manual XSS risks~~ (False finding)
|
|
85 |
+ |
All user content (feed titles, authors, descriptions, tags) escaped with escapeHtml()/escapeAttr(). Rich HTML body uses sanitizeHtml(). Verified 2026-04-22.
|
|
86 |
+ |
|
|
87 |
+ |
### 4. ~~FTS query injection~~ (False finding)
|
|
88 |
+ |
sanitize_fts_query wraps every word in double quotes, which neutralizes all FTS5 operators including NEAR and column: prefixes. Tests confirm this at lines 1862-1874 of repository.rs. Verified 2026-04-22.
|
|
89 |
+ |
|
|
90 |
+ |
### 5. ~~Sparse documentation~~ (Fixed)
|
|
91 |
+ |
All public items now have `///` doc comments. 14 doc files in docs/. Fixed 2026-04-22.
|
|
92 |
+ |
|
|
93 |
+ |
## Competitive Comparison
|
|
94 |
+ |
|
|
95 |
+ |
Based on `docs/apps/bb/competition.md`, BB holds a unique position as the only native desktop feed reader with a user-scriptable plugin system.
|
|
96 |
+ |
|
|
97 |
+ |
**Gaps closed since the competition analysis was written:**
|
|
98 |
+ |
- Full-text search, Tags/categories, URL tracker stripping, JSON Feed format, Feed config validation, Secret encryption, Feed health monitoring, Stale item cleanup, Theming, Cloud sync
|
|
99 |
+ |
|
|
100 |
+ |
**Remaining competitive gaps:**
|
|
101 |
+ |
1. Reader view / full-article fetch -- planned as a plugin (Phase 5)
|
|
102 |
+ |
2. Filter/query feeds (virtual feeds from rules) -- Phase 5
|
|
103 |
+ |
3. Mobile support -- deferred (Tauri mobile)
|
|
104 |
+ |
|
|
105 |
+ |
## Action Items
|
|
106 |
+ |
|
|
107 |
+ |
### Run 15 (2026-04-18, corrected 2026-04-22)
|
|
108 |
+ |
1. ~~**[CRITICAL]** Investigate test regression~~ -- False finding. 601 tests with `--workspace`.
|
|
109 |
+ |
2. ~~**[HIGH]** Add tests to bb-feed~~ -- False finding. bb-feed has 110 tests.
|
|
110 |
+ |
3. ~~**[MEDIUM]** Audit and fix manual XSS gaps~~ -- False finding. All content properly escaped.
|
|
111 |
+ |
4. ~~**[MEDIUM]** Add tracing instrumentation to db/feed modules~~ -- Done (174 annotations added).
|
|
112 |
+ |
5. ~~**[LOW]** Harden FTS query sanitization~~ -- False finding. Already secure (terms quoted).
|
|
113 |
+ |
6. ~~**[LOW]** Improve documentation coverage~~ -- Done. All public items documented.
|
|
114 |
+ |
|
|
115 |
+ |
### Previous action items
|
|
116 |
+ |
- ~~Circuit breaker~~ -- FIXED (migration 008, threshold 10, skip in auto-fetch, reset API)
|
|
117 |
+ |
- Changelog maintenance -- PARTIALLY FIXED
|
|
118 |
+ |
- ~~FTS injection~~ -- RESOLVED (was already secure, terms quoted)
|
|
119 |
+ |
|
|
120 |
+ |
### Resolved (prior audits)
|
|
121 |
+ |
- ~~Fix single-quote XSS in tag filter~~ -- sources.js uses addEventListener
|
|
122 |
+ |
- ~~Set 0600 permissions on encryption.key~~ -- crypto.rs sets 0o600
|
|
123 |
+ |
- ~~Replace `.expect("poisoned")`~~ -- replaced with error propagation
|
|
124 |
+ |
- ~~Remove deprecated health stubs~~ -- removed
|
|
125 |
+ |
- ~~Add `data:` URI blocking~~ -- utils.js blocks both javascript: and data:
|
|
126 |
+ |
- ~~bb-feed generator tests~~ -- tag filtering tests added
|
|
127 |
+ |
- ~~Tauri command integration tests~~ -- 50 tests added
|
|
128 |
+ |
- ~~Plugin authoring guide~~ -- included in README
|
|
129 |
+ |
- ~~Fix clippy violations~~ -- all resolved
|
|
130 |
+ |
- ~~Add repository doc comments~~ -- 46+ methods documented
|
|
131 |
+ |
- ~~Unify pagination strategy~~ -- standardized page_size+1
|
|
132 |
+ |
- ~~Entity ID newtypes~~ -- FeedId, ItemId, BusserStateId, BusserId
|
|
133 |
+ |
- ~~ApiErrorCode enum~~ -- replaces String error codes
|
| 66 |
134 |
|
|
| 67 |
135 |
|
## Metrics Over Time
|
| 68 |
136 |
|
|
| 69 |
|
- |
| Metric | Audit 1 (2026-02-27) | Audit 2 (2026-02-28) | Audit 3 (2026-02-28) | Audit 4 (2026-03-01) | Audit 5 (2026-03-11) | Audit 6 (2026-03-13) | Adversarial (2026-03-13) | 2026-03-22 |
|
| 70 |
|
- |
|--------|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|
|
| 71 |
|
- |
| Overall Grade | A | A- | A- | A- | A- | A- | A- | A | A |
|
| 72 |
|
- |
| Tests | 178 | 225 | 225 | 315 | 359 | 520 | 536 | 599 | 602 |
|
| 73 |
|
- |
| Clippy warnings | 0 | 0 | 8 | 1 | 0 | 0 | 0 | 0 | 0 |
|
| 74 |
|
- |
| Unwrap/expect (prod) | 6 | 3 | 3 | 3 | 4 | 7 | 7 | 7+111 | 7+111 |
|
| 75 |
|
- |
| Rust source LOC | ~5,400 | ~5,400 | ~5,400 | ~5,400 | ~7,600 | ~7,600 | ~7,600 | ~7,600 | ~7,600 |
|
| 76 |
|
- |
| JS LOC | 1,658 | 1,658 | 1,658 | 1,658 | 2,140 | 2,140 | 2,140 | 2,140 | 2,140 |
|
| 77 |
|
- |
| Cold spots | 5 | 5 | 2 | 8 | 5 | 4 | 4 | 1 | 0 |
|
|
137 |
+ |
| Metric | Audit 1 (02-27) | Audit 2 (02-28) | Audit 3 (02-28) | Audit 4 (03-01) | Audit 5 (03-11) | Audit 6 (03-13) | Adversarial (03-13) | 03-22 | Run 12 (03-28) | Run 14 (04-15) | Run 15 (04-18) | Run 15 corrected (04-22) |
|
|
138 |
+ |
|--------|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|
|
|
139 |
+ |
| Overall Grade | A | A- | A- | A- | A- | A- | A- | A | A | A | B+ | A |
|
|
140 |
+ |
| Tests | 178 | 225 | 225 | 315 | 359 | 520 | 536 | 599 | 602 | 602 | 210 | 601 |
|
|
141 |
+ |
| Clippy warnings | 0 | 0 | 8 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
|
|
142 |
+ |
| Unwrap/expect (prod) | 6 | 3 | 3 | 3 | 4 | 7 | 7 | 7+111 | 7+111 | 7+111 | 7+111 | 7+111 |
|
|
143 |
+ |
| Rust source LOC | ~5,400 | ~5,400 | ~5,400 | ~5,400 | ~7,600 | ~7,600 | ~7,600 | ~7,600 | ~7,600 | ~7,600 | ~23,458 | ~23,458 |
|
|
144 |
+ |
| JS LOC | 1,658 | 1,658 | 1,658 | 1,658 | 2,140 | 2,140 | 2,140 | 2,140 | 2,140 | 2,140 | 2,140 | 2,140 |
|
|
145 |
+ |
| Cold spots | 5 | 5 | 2 | 8 | 5 | 4 | 4 | 1 | 0 | 0 | 4 | 0 |
|
| 78 |
146 |
|
|
| 79 |
147 |
|
---
|
| 80 |
148 |
|
|
| 82 |
150 |
|
|
| 83 |
151 |
|
---
|
| 84 |
152 |
|
|
| 85 |
|
- |
## Strengths
|
| 86 |
|
- |
|
| 87 |
|
- |
- **Zero `.unwrap()` in business logic.** Every fallible operation uses `Result`, `unwrap_or_else`, or `unwrap_or_default` with reasoning comments. Only 4 unwrap/expect in startup/static contexts.
|
| 88 |
|
- |
|
| 89 |
|
- |
- **All SQL is parameterized.** Every query in `repository.rs` (1,822 lines) uses `?N` placeholders with `.bind()`. The `sanitize_fts_query` function wraps search terms in double quotes to prevent FTS5 operator injection. Dynamic placeholder strings built safely.
|
| 90 |
|
- |
|
| 91 |
|
- |
- **Defense-in-depth security.** Multiple independent layers: Rhai engine sandboxed (100k ops, depth 128), AES-256-GCM encryption for secrets at rest with versioned format, HTML sanitization strips dangerous elements, XML escaping in OPML export, URL tracker parameter stripping, extensive input validation.
|
| 92 |
|
- |
|
| 93 |
|
- |
- **Clean crate boundaries.** `bb-interface` defines types/traits with zero impl deps. `bb-db` handles persistence with no plugin knowledge. `bb-core` orchestrates without touching SQL. `bb-feed` handles aggregation. No circular dependencies.
|
| 94 |
|
- |
|
| 95 |
|
- |
- **Comprehensive test suite (602 tests).** Coverage spans crypto roundtrips, FTS5 search, feed health, tag CRUD, stale cleanup, upsert conflict, plugin manager lifecycle, Rhai conversions (50+ tests), ordering (25 tests), generator pagination, command integration (50 tests), sync service.
|
| 96 |
|
- |
|
| 97 |
|
- |
- **Well-designed sync integration.** FK-safe ordering on remote changes, table column whitelists preventing arbitrary data injection, `applying_remote` flag prevents changelog loops, exponential backoff with 15-minute cap.
|
| 98 |
|
- |
|
| 99 |
|
- |
## Weaknesses
|
| 100 |
|
- |
|
| 101 |
|
- |
- ~~**Rhai HTTP functions with no limits.**~~ All 4 gaps resolved: request count limit (100/fetch), response size cap (2 MB), URL scheme + private range blocking, aggregate fetch timeout (60s).
|
| 102 |
|
- |
|
| 103 |
|
- |
- ~~**No circuit breaker.**~~ Circuit breaker implemented (migration 008, threshold 10, skip in auto-fetch, reset API, event emission).
|
| 104 |
|
- |
|
| 105 |
|
- |
- ~~**Sync changelog unbounded growth.**~~ Changelog cap at 10,000 entries with `enforce_changelog_retention()` on sync tick.
|
| 106 |
|
- |
|
| 107 |
|
- |
- ~~**Zero automated JS tests.**~~ 55 JS tests covering state management, utilities, sources sidebar rendering, items rendering, and settings-sync 4-state flow.
|
| 108 |
|
- |
|
| 109 |
|
- |
- **FTS query sanitization gaps.** `sanitize_fts_query` strips standard operators but may not cover `NEAR` or `column:` prefix syntax. Low risk (local DB) but incomplete.
|
| 110 |
|
- |
|
| 111 |
|
- |
## Competitive Comparison
|
| 112 |
|
- |
|
| 113 |
|
- |
Based on `docs/apps/bb/competition.md`, BB holds a unique position as the only native desktop feed reader with a user-scriptable plugin system.
|
| 114 |
|
- |
|
| 115 |
|
- |
**Gaps closed since the competition analysis was written:**
|
| 116 |
|
- |
- Full-text search: Implemented via FTS5
|
| 117 |
|
- |
- Tags/categories: Feed tags with junction table, sidebar filter bar
|
| 118 |
|
- |
- URL tracker stripping: Implemented in `url_cleaner.rs`
|
| 119 |
|
- |
- JSON Feed format: Implemented in `conversions.rs` (1.0/1.1)
|
| 120 |
|
- |
- Feed config validation: Schema-aware validation in `create_feed`
|
| 121 |
|
- |
- Secret encryption: AES-256-GCM with versioned format
|
| 122 |
|
- |
- Feed health monitoring: consecutive_failures tracking, health dots
|
| 123 |
|
- |
- Stale item cleanup: Background task every 6h
|
| 124 |
|
- |
- Theming: 9 TOML themes, bundled + user custom
|
| 125 |
|
- |
- Cloud sync: SyncKit integration with E2E encryption
|
| 126 |
|
- |
|
| 127 |
|
- |
**Remaining competitive gaps (from competition.md priority list):**
|
| 128 |
|
- |
1. Reader view / full-article fetch -- planned as a plugin (Phase 5)
|
| 129 |
|
- |
2. Filter/query feeds (virtual feeds from rules) -- Phase 5
|
| 130 |
|
- |
3. Mobile support -- deferred (Tauri mobile)
|
| 131 |
|
- |
|
| 132 |
|
- |
## Action Items
|
| 133 |
|
- |
|
| 134 |
|
- |
All resolved from prior audits. New items from fifth audit filed in `docs/apps/bb/todo.md`.
|
| 135 |
|
- |
|
| 136 |
|
- |
### Resolved (prior audits)
|
| 137 |
|
- |
- ~~Fix single-quote XSS in tag filter~~ -- sources.js uses addEventListener
|
| 138 |
|
- |
- ~~Set 0600 permissions on encryption.key~~ -- crypto.rs sets 0o600
|
| 139 |
|
- |
- ~~Replace `.expect("poisoned")`~~ -- replaced with error propagation
|
| 140 |
|
- |
- ~~Remove deprecated health stubs~~ -- markFailed/clearFailed/clearAllFailed removed
|
| 141 |
|
- |
- ~~Add `data:` URI blocking~~ -- utils.js blocks both javascript: and data:
|
| 142 |
|
- |
- ~~bb-feed generator tests~~ -- tag filtering tests added
|
| 143 |
|
- |
- ~~Tauri command integration tests~~ -- 50 tests added
|
| 144 |
|
- |
- ~~README with setup instructions~~ -- README.md at project root
|
| 145 |
|
- |
- ~~Plugin authoring guide~~ -- included in README
|
| 146 |
|
- |
- ~~Fix 8 clippy violations~~ -- all resolved
|
| 147 |
|
- |
- ~~Fix `.env` PostgreSQL URL~~ -- changed to sqlite
|
| 148 |
|
- |
- ~~Fix clippy warning (unused OrderBy import)~~ -- removed
|
| 149 |
|
- |
- ~~Add repository doc comments~~ -- 46+ methods documented
|
| 150 |
|
- |
- ~~Unify pagination strategy~~ -- standardized page_size+1
|
| 151 |
|
- |
- ~~Add background task unit tests~~ -- any_feed_due extracted + tested
|
| 152 |
|
- |
- ~~Add search loading indicator~~ -- CSS spinner added
|
| 153 |
|
- |
- ~~Entity ID newtypes~~ -- FeedId, ItemId, BusserStateId, BusserId
|
| 154 |
|
- |
- ~~ApiErrorCode enum~~ -- replaces String error codes
|
| 155 |
|
- |
|
| 156 |
|
- |
---
|
| 157 |
|
- |
|
| 158 |
153 |
|
## Documentation Review
|
| 159 |
154 |
|
|
| 160 |
155 |
|
**Last reviewed:** 2026-03-04 (first doc audit)
|
| 161 |
156 |
|
|
| 162 |
|
- |
### Overall Doc Grade: A
|
|
157 |
+ |
### Overall Doc Grade: B-
|
| 163 |
158 |
|
|
| 164 |
|
- |
Clean doc set. README and plugin_authoring.md are good additions from recent audit work. Main issue was stale test count in todo.md (106 vs 225 actual). description.md is an intentional placeholder.
|
|
159 |
+ |
Doc set exists but is sparse in some areas. Changelog only partially maintained. Module docs need expansion.
|
| 165 |
160 |
|
|
| 166 |
161 |
|
### Document Heatmap
|
| 167 |
162 |
|
|
| 168 |
163 |
|
| Document | Status | Last Verified | Notes |
|
| 169 |
164 |
|
|----------|:------:|:-------------:|-------|
|
| 170 |
|
- |
| docs/apps/bb/todo.md | Current | 2026-03-11 | Updated with audit 5 action items |
|
|
165 |
+ |
| docs/apps/bb/todo.md | Current | 2026-04-18 | Active task list |
|
| 171 |
166 |
|
| docs/apps/bb/description.md | Placeholder | 2026-03-04 | Intentional placeholder |
|
| 172 |
167 |
|
| docs/apps/bb/competition.md | Current | 2026-03-04 | Competitive analysis |
|
| 173 |
|
- |
| docs/apps/bb/structural_metrics.md | New | 2026-03-11 | Structural metrics from audit 5 |
|
| 174 |
|
- |
| docs/apps/bb/stress_test.md | New | 2026-03-11 | Phase 4/5 stress test |
|
|
168 |
+ |
| docs/apps/bb/structural_metrics.md | Stale | 2026-03-11 | Needs update for current LOC/test counts |
|
|
169 |
+ |
| docs/apps/bb/stress_test.md | Current | 2026-03-11 | Phase 4/5 stress test |
|
| 175 |
170 |
|
| README.md | Current | 2026-03-04 | Setup instructions |
|
| 176 |
171 |
|
|
| 177 |
|
- |
### Stale References Found (Doc Audit)
|
| 178 |
|
- |
|
| 179 |
|
- |
| Location | Issue | Resolution |
|
| 180 |
|
- |
|----------|-------|------------|
|
| 181 |
|
- |
| docs/apps/bb/todo.md | Status line says "106 tests passing" -- actual is 225 | Updated to "225 tests passing" (audit 4) |
|
| 182 |
|
- |
| docs/apps/bb/todo.md | Status line updated to 359 tests | Updated (audit 5) |
|
| 183 |
|
- |
|
| 184 |
172 |
|
### Doc Action Items
|
| 185 |
173 |
|
|
| 186 |
|
- |
- None critical. Test count kept in sync.
|
|
174 |
+ |
- Update structural_metrics.md for current LOC/test counts
|
|
175 |
+ |
- Expand module-level documentation
|