Skip to content

fix(audit): harden local browser boundaries#303

Merged
bnema merged 12 commits into
mainfrom
fix/audit-hardening
Jun 12, 2026
Merged

fix(audit): harden local browser boundaries#303
bnema merged 12 commits into
mainfrom
fix/audit-hardening

Conversation

@bnema

@bnema bnema commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

  • Harden filter downloads by validating manifest filenames, bounding manifest/filter payload sizes, and confining nested cache writes/lookups with os.Root.
  • Make favicon cache shutdown no-op safe, clamp WebUI history query limits, and restrict the WebKit native bridge to trusted dumb:// pages.
  • Pin lint/staticcheck tooling, add generated-asset CI verification, update source-build docs, and remove the orphan root package-lock.json.

Closes #286.
Closes #287.
Closes #288.
Closes #289.
Closes #291.
Closes #292.
Closes #293.
Closes #294.
Closes #298.
Closes #299.

Test Plan

  • go test ./...
  • make verify-generated
  • git diff --check

Review

  • go-reviewer initial pass
  • go-reviewer re-review after symlink containment fix

Summary by CodeRabbit

  • Bug Fixes

    • Prevented cache writes during shutdown; tightened shutdown safety.
    • Consistent pagination/limit clamping for search/history APIs.
    • Hardened downloads and manifest/filter validation with filesystem/path safety and byte limits.
    • Safer HTML attribute handling in systemviews templates; improved message/WebView sender validation.
  • Refactor

    • Safer window/input teardown: new teardown-safe detach APIs and consolidated destroy helpers.
  • Chores

    • Pinned dev tools and CI Go setup, added verify-generated and staticcheck targets; updated docs and build prerequisites (Go 1.26+, Brotli; removed Node requirement).
  • Tests

    • Expanded tests for clamping, downloads/path-safety, cache shutdown, teardown behavior, debounce timing, and trusted-message checks.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Pins CI/local Go tooling, verifies generated systemviews are committed, centralizes history limit clamping with tests, makes favicon cache close-safe, hardens the filter downloader (size/path limits + tests), restricts WebKit bridge to trusted URIs, updates generated templ attribute resolution, and adds destroy-safe input detach APIs with tests.

Changes

Build system, CI workflow, and documentation alignment

Layer / File(s) Summary
Makefile pinned tooling and verification targets
Makefile
Adds TOOL_BIN_DIR and pinned golangci-lint/staticcheck versions, dedicated install targets, staticcheck target, and verify-generated target that diffs generated systemviews artifacts.
CI workflow updates for pinned linting and artifact verification
.github/workflows/ci.yml
CI lint job sets up Go from go.mod, runs make build, make verify-generated, and uses make install-golangci-lint; test job sets up Go from go.mod before build/test.
Documentation updates for Go 1.26 and systemviews assets
README.md, docs/installation.md
Bumps Go requirement to 1.26+, adds Brotli/systemviews notes, removes Node.js frontend prerequisite, documents pinned tooling, and updates build/run instructions to use make build and ./dist/dumber browse.

Search history pagination and query limit clamping

Layer / File(s) Summary
History constants and clamping helpers
internal/application/usecase/search_history.go
Adds default/max constants and clampOptionalLimit / clampPositiveLimit helpers.
Use-case methods apply limit clamping
internal/application/usecase/search_history.go
Search, GetRecent, GetRecentByDomain, and GetDomainStats clamp request limits before repository calls.
Limit clamping test coverage
internal/application/usecase/search_history_test.go
New tests assert oversized limits are clamped for GetRecent, Search, GetDomainStats, and GetRecentByDomain.

Favicon cache close-safe shutdown

Layer / File(s) Summary
Cache struct and shutdown state tracking
internal/infrastructure/favicon/cache.go
Adds internal closed bool to Cache struct.
Cache methods check shutdown state before write operations
internal/infrastructure/favicon/cache.go
Set, Close, and queueDiskWrite observe closed under mutex/read-lock to avoid writes during shutdown.
Cache shutdown no-op regression test
internal/infrastructure/favicon/cache_test.go
New test ensures Set after Close is a no-op and Get does not return values written post-close.

Filter downloader security hardening: size limits, filename validation, and path escape prevention

Layer / File(s) Summary
Download limits configuration and initialization
internal/infrastructure/filtering/downloader.go
Introduces downloadLimits and stores limits on Downloader (manifest size, max files, per-file bytes, total bytes).
Manifest validation with size-bounded reading
internal/infrastructure/filtering/downloader.go
GetCachedManifest and FetchManifest use size-limited readers and validate manifests via validateManifest.
Filter file download with size bounds and safe path handling
internal/infrastructure/filtering/downloader.go
DownloadFilters enforces total-download budget; downloadFile accepts maxBytes, validates/sanitizes filenames, prechecks Content-Length, copies via copyLimited to temp files, and renames with root-scoped renameTempFile.
Manifest and cached path access with validation
internal/infrastructure/filtering/downloader.go
cacheManifest validates before writing; GetCachedFilterPaths validates filenames, returns nil on invalid entries, and uses hasCachedFilterFile for safe existence checks.
Filesystem-safety helpers and bounded I/O utilities
internal/infrastructure/filtering/downloader.go
Adds validateFilterFilename, cachePathForFilterFile, ensurePathInside, size-limited readers, copyLimited, hasCachedFilterFile, and supporting helpers.
Downloader security test suite and adjustments
internal/infrastructure/filtering/downloader_test.go
Adds tests covering unsafe manifest filenames, safe nested filenames, oversized manifest/file rejection, total-download cap enforcement, symlink-escape resistance, and adjusts renameTempFile expectations.

WebKit native bridge origin validation and trusted-page restriction

Layer / File(s) Summary
Trusted bridge validation helper functions
internal/infrastructure/webkit/messaging.go
Adds isTrustedBridgeWebView and isTrustedBridgeURI delegating to isTrustedSystemviewURL.
Script message handler sender origin validation
internal/infrastructure/webkit/messaging.go
handleScriptMessage resolves sender WebView from UserContentManager, rejects unknown/untrusted senders, overwrites msg.WebViewID from trusted sender, and syncs router mapping before dispatch.
Trusted bridge URI whitelist test coverage
internal/infrastructure/webkit/messaging_test.go
New parallel test validates trusted and untrusted URI inputs for isTrustedBridgeURI.

Systemviews templ-generated attribute resolution

Layer / File(s) Summary
Components templates
internal/ui/systemviews/components_templ.go
Multiple dynamic attributes switched to templ.ResolveAttributeValue(...) and written directly for consistent attribute resolution.
Favorites templates
internal/ui/systemviews/favorites_templ.go
Favorites-related attribute and value renderings now use templ.ResolveAttributeValue(...).
History and icons templates
internal/ui/systemviews/history_templ.go, internal/ui/systemviews/icons_templ.go
History attributes (data-*, favicons, titles) and Icon class attribute updated to use templ.ResolveAttributeValue(...).

Browser window teardown and input handler destroy-safety

Layer / File(s) Summary
browserWindow teardown helpers and cleanup flow
internal/ui/browser_window.go, internal/ui/app.go, internal/ui/browser_window_test.go
Adds teardownForDestroy/cleanupTransientBrowserWindowForDestroy, updates App.removeBrowserWindow usage, and adjusts stale-window destroy ordering; adds test for transient-window cleanup.
Keyboard, gesture, and global-shortcut detach APIs
internal/ui/input/gesture.go, internal/ui/input/global_shortcuts.go, internal/ui/input/keyboard.go
Add DetachForDestroy() and shared private detach helpers to avoid mutating GTK controller lists during widget/window destruction; destroy callbacks updated to call destroy-safe detach.
Input handler teardown tests
internal/ui/input/gesture_test.go, internal/ui/input/global_shortcuts_test.go, internal/ui/input/keyboard_test.go
New tests verify DetachForDestroy() clears retained callbacks/state for gesture, global shortcut, and keyboard handlers.

Watcher debounce timing

Layer / File(s) Summary
Watcher debounce timing update
internal/infrastructure/externaltheme/noctalia/watcher_test.go
Updates test to use NewFileWatcher() and increases debounce/assertion timing to 100ms/150ms.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • bnema/dumber#205: Overlaps on downloader manifest validation and cached-path handling changes in internal/infrastructure/filtering/downloader.go.
  • bnema/dumber#281: Related changes to browser-window/input teardown flow (internal/ui/browser_window.go, internal/ui/input/global_shortcuts.go).
  • bnema/dumber#263: Related to GlobalShortcutHandler teardown/Detach behavior and tests.

"I’m a rabbit with a dev-time hat,
I pin the tools and check the map,
I guard the cache and bound the streams,
I tidy templates and hush the teams,
Hop safe—your builds and bridges clap!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(audit): harden local browser boundaries' directly relates to the main objective of hardening security boundaries for local browser operations, which is the central theme across all linked issues.
Linked Issues check ✅ Passed The PR successfully addresses all 10 linked issues: filter path sanitization and size bounds (#286, #287), favicon cache safety (#288), history query limits (#289), golangci-lint pinning (#291), CI verification of generated assets (#292), documentation updates for Go 1.26 (#293), WebKit bridge restriction (#294), staticcheck alignment (#298), and orphan lockfile removal (#299).
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked audit issues: infrastructure hardening, tooling version pinning, documentation updates, and CI verification steps. No unrelated refactoring or feature additions are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/audit-hardening

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 105-106: The workflow runs the make target make verify-generated
in both the lint and test jobs, causing redundant verification; either keep the
duplication with an explicit comment explaining the defense-in-depth rationale
or remove one invocation so verification only runs once (e.g., keep it in the
lint job). Update the .github/workflows/ci.yml to either remove the run: make
verify-generated step from the test job (or from the lint job) or add a clear
inline comment near the verify-generated steps stating that duplication is
intentional for safety/defense-in-depth, referencing the verify-generated target
and the lint and test jobs so future editors understand the reason.
- Around line 70-71: The workflow hardcodes TOOL_BIN_DIR=/usr/local/bin when
running the Makefile target install-golangci-lint, which can be unwritable;
remove the override so the Makefile default (TOOL_BIN_DIR?=$(shell go env
GOPATH)/bin) is used, or set TOOL_BIN_DIR to the GOPATH-based path instead;
update the GitHub Action step that calls make install-golangci-lint to omit the
TOOL_BIN_DIR argument (or compute and pass the GOPATH/bin value) so the
Makefile’s default behavior is preserved.

In `@Makefile`:
- Around line 158-161: The Makefile target install-golangci-lint uses a
versioned path for the install script which can mismatch the passed
$(GOLANGCI_LINT_VERSION); update the install command in the
install-golangci-lint target to fetch the stable install script (use "master" in
the URL) and keep passing $(GOLANGCI_LINT_VERSION) as the argument so the
installer always runs the same script regardless of the version variable; modify
the install line that references GOLANGCI_LINT_VERSION and TOOL_BIN_DIR
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0f52cde3-92cc-4859-a98a-710dd32d2765

📥 Commits

Reviewing files that changed from the base of the PR and between 5eba8fd and a19ed71.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • .github/workflows/ci.yml
  • Makefile
  • README.md
  • docs/installation.md
  • internal/application/usecase/search_history.go
  • internal/application/usecase/search_history_test.go
  • internal/infrastructure/favicon/cache.go
  • internal/infrastructure/favicon/cache_test.go
  • internal/infrastructure/filtering/downloader.go
  • internal/infrastructure/filtering/downloader_test.go
  • internal/infrastructure/webkit/messaging.go
  • internal/infrastructure/webkit/messaging_test.go

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread Makefile Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 52-56: The workflow currently uses the mutable tag
actions/setup-go@v6 in the "Set up pinned Go toolchain" step (and the equivalent
setup step in the test job) which should be pinned to an immutable commit SHA;
replace actions/setup-go@v6 with actions/setup-go@<commit-sha> (the full 40-char
SHA for the v6 tag) in both occurrences, using the gh api command shown in the
review to fetch the correct tag SHA (or copy the SHA from the official
actions/setup-go repo) so the workflow references an immutable commit instead of
a mutable tag.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f4b00786-24d5-4f41-adab-59874277732f

📥 Commits

Reviewing files that changed from the base of the PR and between 0f5f939 and a7abb18.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • internal/infrastructure/externaltheme/noctalia/watcher_test.go

Comment thread .github/workflows/ci.yml
@bnema

bnema commented Jun 11, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/ci.yml (1)

1-3: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add explicit least-privilege permissions for the workflow token.

This workflow still uses default token permissions; define explicit read-only permissions to reduce CI supply-chain blast radius.

🔒 Suggested patch
 name: CI
 
+permissions:
+  contents: read
+
 on:
   push:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1 - 3, Add an explicit least-privilege
permissions block to the "CI" workflow by inserting a top-level permissions key
(for example permissions: contents: read) immediately under the workflow header
so the workflow token is limited to read-only access to the repository contents;
update the CI workflow (name: CI) to include this permissions block rather than
relying on default token permissions.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/ci.yml:
- Around line 1-3: Add an explicit least-privilege permissions block to the "CI"
workflow by inserting a top-level permissions key (for example permissions:
contents: read) immediately under the workflow header so the workflow token is
limited to read-only access to the repository contents; update the CI workflow
(name: CI) to include this permissions block rather than relying on default
token permissions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 229c29c2-eb2d-4200-837c-be833405d89e

📥 Commits

Reviewing files that changed from the base of the PR and between 4b8e76f and 7ed1723.

📒 Files selected for processing (12)
  • .github/workflows/ci.yml
  • Makefile
  • internal/application/usecase/search_history.go
  • internal/ui/app.go
  • internal/ui/browser_window.go
  • internal/ui/browser_window_test.go
  • internal/ui/input/gesture.go
  • internal/ui/input/gesture_test.go
  • internal/ui/input/global_shortcuts.go
  • internal/ui/input/global_shortcuts_test.go
  • internal/ui/input/keyboard.go
  • internal/ui/input/keyboard_test.go

@bnema bnema merged commit c98d739 into main Jun 12, 2026
5 checks passed
@bnema bnema deleted the fix/audit-hardening branch June 12, 2026 00:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment