feat(analyze-problem): download attachments/screenshots behind a mandatory security gate (#725)#726
Conversation
Technical report — pre-PR self-reviewCode review summaryInline self-review against the project rules during the implementation loop. The
The authoritative Security review summarySelf-review against
Residual (documented in PR TODO): SSRF surface inherent to fetching issue-supplied
|
Code review — resolved items (converged: 0 Critical, 0 Moderate)Processed the review on this PR. Convergence reached in 2 loop iterations; final re-review reports 0 Critical, 0 Moderate. No reviewer review-threads were open (the CR was a general comment), so none required resolution.
|
Code Review — PR #726 (final diff)Status: ✅ Converged — ready to merge All findings from the prior review iteration were resolved (see the
Specialized reviews self-skipped (no matching surface): api-review, mysql-problem-solver, dependency-selection, assignment-compliance-check (no gaps), Summary: assignment conformance: conformant. The change keeps TLS validation on, the auth token out of argv/manifest/logs, guards every outbound URL against SSRF to non-public hosts, and quarantines every download behind the deterministic security gate before promotion to |
…gate Add skills/_shared/attachments.sh (sourced download library) and skills/_shared/scan-attachments.sh (standalone, deterministic security gate). The library downloads tracker attachments into a 0600 quarantine dir with TLS validation always on (--proto/--proto-redir =https, never -k), reads auth only from a 0600 curl --config file (token never in argv/logs), caps transfer size, writes attachments-manifest.json, then runs the scan gate. The gate classifies every file from magic bytes + a bounded content sniff and blocks executables, archives, scripts, HTML, SVG with active content, polyglots, declared/actual MIME mismatches, and over-limit files; only allowlisted, inert types (png/jpg/gif/webp/pdf/txt/log/csv/json) are promoted to safe/. Ships a --self-test that proves a benign PNG passes and malicious SVG/HTML/polyglot are blocked. Refs #725
Add download-attachments.sh for JIRA, GitHub, and Bugsnag, each delegating the download + scan to skills/_shared/attachments.sh. - JIRA: HTTP Basic email:token resolved from --token-file / JIRA_API_TOKEN / ~/.config/acli/jira_api_token (+ JIRA_API_EMAIL); a missing token exits non-zero with a setup hint, never a silent skip. - GitHub: extracts inline attachment URLs from the issue/PR body and comments (github.com / githubusercontent hosts only) and authenticates with gh auth token, sent to github.com only (-L, not --location-trusted). - Bugsnag: BUGSNAG_TOKEN authenticates the API read only; comment-linked URLs are fetched unauthenticated so the org token never reaches a third-party host. Refs #725
… -> safe-only order The mandatory pre-flight now requires running the tracker's download-attachments.sh and reading only files the security gate promoted to safe/. Documents the auth setup per tracker and the block/review handling (blocked or review-pending attachments are recorded in Assumptions/Sources, never opened). Updates the JIRA gatherer note that previously told the agent to fetch contentUrl directly. Refs #725
…to SKILL.md dirs Pin the three download-attachments.sh wrappers and the shared library/gate as shipped, executable, and documented; assert TLS validation is never disabled and the token stays out of argv; assert the gate blocks the dangerous categories, enforces the allowlist and limits, and self-tests; assert analyze-problem documents the mandatory order. Count only directories that ship a SKILL.md as skills (matching skill-check) so the shared helper dir skills/_shared/ does not inflate the README skill count. Refs #725
Address CR Moderate: a user-supplied inventory URL (notably a Bugsnag comment link) could drive an outbound request to an internal service. att_run now runs att_host_block_reason on every URL before downloading and marks loopback, link-local (incl. the 169.254.169.254 cloud-metadata endpoint), and RFC-1918 / ULA private hosts as blocked in the manifest — no request is issued. Self-hosted trackers on a private network opt out with ATT_ALLOW_PRIVATE_HOSTS=1. Refs #725
…pression Address CR Minor findings: scan-attachments.sh now removes its per-entry temp file and exits 3 when the manifest update jq fails, instead of leaking it under set -e; the download wrappers annotate why the loader / gh-auth stderr is suppressed (the result is validated on the next line). Refs #725
5838edc to
d28674f
Compare
Code Review — PR #726 (re-review of the final diff after rebase)Status: ✅ Converged — ready to merge Re-reviewed after the branch was rebased onto
CI Quality Checks (PHP 8.5) is green on Summary: assignment conformance: conformant. TLS stays on, the token never reaches argv/manifest/logs, every outbound URL is SSRF-guarded against non-public hosts, and every download is quarantined behind the deterministic security gate before promotion to |
Summary
Adds reliable, security-gated downloading of issue-tracker attachments/screenshots so
analyze-problemno longer runs blind. Bytes are downloaded into a 0600 quarantine with TLS validation always on, then a mandatory deterministic security gate classifies every file; only safe, allowlisted, inert types are promoted tosafe/for the analysis to read. Resolves #725.What changed
skills/_shared/attachments.sh— sourced download library. Fetches each inventory URL into<dest>/_quarantine/(0600), TLS pinned to HTTPS (--proto/--proto-redir '=https', never-k), auth read only from a 0600 curl--configfile (token never in argv/logs), size-capped per transfer, then writesattachments-manifest.jsonand invokes the scan gate.skills/_shared/scan-attachments.sh— standalone, deterministic security gate. Classifies from magic bytes + a bounded content sniff and blocks executables, archives, scripts, HTML, SVG with active content, polyglots, declared/actual MIME mismatches, and over-limit files; promotes only allowlisted inert types (png/jpg/gif/webp/pdf/txt/log/csv/json) tosafe/. Ships--self-test.skills/code-review-{github,jira,bugsnag}/scripts/download-attachments.sh— thin per-tracker wrappers (auth + inventory) delegating to the shared library.skills/analyze-problem/SKILL.md— pre-flight now enforces inventory → download → security gate → analyse onlysafe/, documents per-tracker auth setup, and the block/review handling.Security posture (rules/security/*)
-k/--insecure/--no-check-certificate/verify=falseanywhere (pinned by test).curl … | sh— responses go to--output, never an interpreter.--configfile — never argv, never the manifest, never a log line. GitHub token sent to github.com only (-L, not--location-trusted); Bugsnag token never forwarded to third-party hosts.Testing
bash skills/_shared/scan-attachments.sh --self-test→ benign PNG promoted; malicious SVG/HTML/polyglot blocked (the fixture proof required by the issue).tests/Installer/SkillsContentTest.php) cover the scripts, the no-disabled-TLS / token-out-of-argv guarantees, the gate's blocked categories + allowlist + limits, and the SKILL.md mandatory order.composer buildgreen (322 tests, 100% coverage on PHP).How to test
gh auth login), then run on an issue with an inline screenshot:skills/code-review-github/scripts/download-attachments.sh <NUMBER|URL>→ inspectattachments-manifest.jsonandsafe/.JIRA_API_EMAIL+ a token source, runskills/code-review-jira/scripts/download-attachments.sh <KEY|URL>.bash skills/_shared/scan-attachments.sh --self-testand confirmPASS.TODO (out of scope / deferred)
download-attachments.shto the installer's bundled-script permission allowlist (InstallerClaudeSettings) so agents don't get a per-call prompt — kept out to avoid touching the pinned allowlist contract in this PR.att_runnow blocks loopback / link-local / private hosts viaatt_host_block_reason(opt-outATT_ALLOW_PRIVATE_HOSTS=1).file-based typing + allowlist still apply); revisit if deeper inspection is needed.