Summary
A basket of small, non-blocking hardening/cleanup follow-ups for the browser tool family (#94, PR #260). Each is independent and low-risk.
Security-adjacent residuals (from the PR #260 re-review of the path-confinement fix)
Minor cleanups
Context
Untracked minors from the PR #260 review. None blocks the PR.
Summary
A basket of small, non-blocking hardening/cleanup follow-ups for the browser tool family (#94, PR #260). Each is independent and low-risk.
Security-adjacent residuals (from the PR #260 re-review of the path-confinement fix)
confinedFilesPath(forge-core/runtime/loop.go) resolves paths withfilepath.Abs+filepath.Rel, but notfilepath.EvalSymlinks. A symlink inside the files dir pointing outside would pass the check (requires an attacker who can already create symlinks in the files dir — low severity). AddEvalSymlinksfor defense in depth.<WorkDir>/.forge-browser/shotswhen no files dir is on the context, but the loop only attaches from the files dir — so screenshots written there are saved and silently never attached. Align the two: either drop the fallback and tell the LLM, or have the loop confine to both known dirs.Minor cleanups
forge-cli/build/requirements_stage.go) dedups by exact namechromium, so a skill declaringgoogle-chromeorchromium-browseras a bin plus the browser capability installs two browsers. Dedup against known chromium aliases..forge-browser-*user-data-dirs under WorkDir leak onSIGKILL(Manager.Stop never runs). Sweep stale ones at Manager init.PolicySummary(forge-skills/analyzer) folds Critical violations intoErrorsbut has no separateCriticalscount; a distinct field would make audits clearer.Context
Untracked minors from the PR #260 review. None blocks the PR.