{"data":{"skill":{"slug":"kochetkov-ma-brewpage-publish","name":"brewpage-publish","icon":"📦","repo":"https://github.com/kochetkov-ma/claude-brewcode/tree/d2d42282fecec988447f9c9547959b31a8012644/skills/brewpage-publish","status":"approved","author":"kochetkov-ma","authorVersion":null,"skillstoreRevision":5},"audit":{"id":"b8a4aa4c-a99b-45ca-b11f-9750da9de55c","skill_id":"4bf85c8e-ddad-4143-9c98-4b5fde9536c8","version":8,"content_hash":"v3:ffcb29632effb1676658b5f2444e67b91c8c7c87:42d0e03a8294e05dd1f4e385c187eea55c633903299d2bd4eb57f0fd4c44efc1:7528029d2ba86bc1cf06d29a242c7549655e32b32b7fc8408fa62166e945db63:736b696c6c732f6b6f636865746b6f762d6d612f62726577706167652d7075626c697368:1d78c56dd5fd18ff670ebca35fa2f170","risk_level":"medium","is_blocked":false,"safe_to_publish":true,"analysis_status":"ok","agent_auto_install_policy":"allowed","manual_install_policy":"allowed","summary":"All 264 static alerts are false positives involving documented publishing, literal Markdown, validation rules, or local safeguards. A separate directory-packaging race can upload unintended files if a concurrent writer replaces validated paths with symlinks. No evidence found of prompt injection, intentional credential theft, or concealed upload destinations.","remediation":[{"issue":"Directory packaging checks file types before zip reopens paths, allowing concurrent replacement with symlinks.","severity":"medium","suggestion":"Copy files into a private snapshot using descriptor-relative traversal, O_NOFOLLOW, and regular-file checks; package only that snapshot and verify its contents."}],"risk_factor_evidence":[{"factor":"network","evidence":[{"file":"LICENSE","line_end":3,"line_start":3},{"file":"README.md","line_end":117,"line_start":117},{"file":"README.md","line_end":127,"line_start":127},{"file":"scripts/brewpage-lib.sh","line_end":51,"line_start":51},{"file":"scripts/brewpage-run.sh","line_end":73,"line_start":73},{"file":"scripts/brewpage-run.sh","line_end":188,"line_start":188},{"file":"scripts/brewpage-run.sh","line_end":194,"line_start":194},{"file":"scripts/brewpage-run.sh","line_end":199,"line_start":199},{"file":"scripts/brewpage-run.sh","line_end":216,"line_start":216},{"file":"SKILL.md","line_end":84,"line_start":84},{"file":"SKILL.md","line_end":198,"line_start":198},{"file":"SKILL.md","line_end":206,"line_start":206},{"file":"SKILL.md","line_end":265,"line_start":265},{"file":"SKILL.md","line_end":266,"line_start":266}]},{"factor":"external_commands","evidence":[{"file":"scripts/brewpage-lib.sh","line_end":51,"line_start":51},{"file":"scripts/brewpage-lib.sh","line_end":52,"line_start":52},{"file":"scripts/brewpage-lib.sh","line_end":62,"line_start":62},{"file":"scripts/brewpage-lib.sh","line_end":65,"line_start":65},{"file":"scripts/brewpage-lib.sh","line_end":166,"line_start":166},{"file":"scripts/brewpage-lib.sh","line_end":187,"line_start":187},{"file":"scripts/brewpage-lib.sh","line_end":8,"line_start":8},{"file":"scripts/brewpage-lib.sh","line_end":9,"line_start":9},{"file":"scripts/brewpage-lib.sh","line_end":10,"line_start":10},{"file":"scripts/brewpage-lib.sh","line_end":21,"line_start":21},{"file":"scripts/brewpage-lib.sh","line_end":32,"line_start":32},{"file":"scripts/brewpage-lib.sh","line_end":37,"line_start":37},{"file":"scripts/brewpage-lib.sh","line_end":47,"line_start":47},{"file":"scripts/brewpage-lib.sh","line_end":63,"line_start":63},{"file":"scripts/brewpage-lib.sh","line_end":66,"line_start":66},{"file":"scripts/brewpage-lib.sh","line_end":105,"line_start":105},{"file":"scripts/brewpage-lib.sh","line_end":190,"line_start":190},{"file":"scripts/brewpage-lib.sh","line_end":192,"line_start":192},{"file":"scripts/brewpage-lib.sh","line_end":199,"line_start":199},{"file":"scripts/brewpage-lib.sh","line_end":201,"line_start":201},{"file":"scripts/brewpage-lib.sh","line_end":202,"line_start":202},{"file":"scripts/brewpage-lib.sh","line_end":204,"line_start":204},{"file":"scripts/brewpage-lib.sh","line_end":248,"line_start":246},{"file":"scripts/brewpage-lib.sh","line_end":265,"line_start":265},{"file":"scripts/brewpage-lib.sh","line_end":65,"line_start":62},{"file":"scripts/brewpage-lib.sh","line_end":166,"line_start":65},{"file":"scripts/brewpage-run.sh","line_end":6,"line_start":6},{"file":"scripts/brewpage-run.sh","line_end":25,"line_start":25},{"file":"scripts/brewpage-run.sh","line_end":47,"line_start":47},{"file":"scripts/brewpage-run.sh","line_end":49,"line_start":49},{"file":"scripts/brewpage-run.sh","line_end":52,"line_start":52},{"file":"scripts/brewpage-run.sh","line_end":55,"line_start":55},{"file":"scripts/brewpage-run.sh","line_end":56,"line_start":56},{"file":"scripts/brewpage-run.sh","line_end":58,"line_start":58},{"file":"scripts/brewpage-run.sh","line_end":61,"line_start":61},{"file":"scripts/brewpage-run.sh","line_end":62,"line_start":62},{"file":"scripts/brewpage-run.sh","line_end":73,"line_start":73},{"file":"scripts/brewpage-run.sh","line_end":103,"line_start":103},{"file":"scripts/brewpage-run.sh","line_end":127,"line_start":127},{"file":"scripts/brewpage-run.sh","line_end":128,"line_start":128},{"file":"scripts/brewpage-run.sh","line_end":129,"line_start":129},{"file":"scripts/brewpage-run.sh","line_end":189,"line_start":188},{"file":"scripts/brewpage-run.sh","line_end":195,"line_start":194},{"file":"scripts/brewpage-run.sh","line_end":199,"line_start":199},{"file":"scripts/brewpage-run.sh","line_end":205,"line_start":205},{"file":"scripts/brewpage-run.sh","line_end":212,"line_start":212},{"file":"scripts/brewpage-run.sh","line_end":216,"line_start":216},{"file":"scripts/brewpage-run.sh","line_end":217,"line_start":217},{"file":"scripts/history.py","line_end":55,"line_start":55},{"file":"scripts/publish.mjs","line_end":4,"line_start":4}]},{"factor":"filesystem","evidence":[{"file":"scripts/brewpage-lib.sh","line_end":84,"line_start":84},{"file":"scripts/brewpage-lib.sh","line_end":10,"line_start":10},{"file":"scripts/brewpage-lib.sh","line_end":14,"line_start":14},{"file":"scripts/brewpage-lib.sh","line_end":28,"line_start":28},{"file":"scripts/brewpage-lib.sh","line_end":38,"line_start":38},{"file":"scripts/brewpage-lib.sh","line_end":8,"line_start":8},{"file":"scripts/brewpage-lib.sh","line_end":9,"line_start":9},{"file":"scripts/brewpage-lib.sh","line_end":10,"line_start":10},{"file":"scripts/brewpage-lib.sh","line_end":21,"line_start":21},{"file":"scripts/brewpage-lib.sh","line_end":46,"line_start":46},{"file":"scripts/brewpage-lib.sh","line_end":132,"line_start":132},{"file":"scripts/brewpage-lib.sh","line_end":133,"line_start":133},{"file":"scripts/brewpage-lib.sh","line_end":148,"line_start":148},{"file":"scripts/brewpage-lib.sh","line_end":160,"line_start":160},{"file":"scripts/brewpage-lib.sh","line_end":183,"line_start":183},{"file":"scripts/brewpage-lib.sh","line_end":196,"line_start":196},{"file":"scripts/brewpage-lib.sh","line_end":212,"line_start":212},{"file":"scripts/brewpage-lib.sh","line_end":93,"line_start":93},{"file":"scripts/brewpage-lib.sh","line_end":226,"line_start":226},{"file":"scripts/brewpage-run.sh","line_end":62,"line_start":62},{"file":"scripts/brewpage-run.sh","line_end":71,"line_start":71},{"file":"scripts/brewpage-run.sh","line_end":77,"line_start":77},{"file":"scripts/brewpage-run.sh","line_end":136,"line_start":136},{"file":"scripts/brewpage-run.sh","line_end":193,"line_start":193},{"file":"scripts/history.py","line_end":33,"line_start":33},{"file":"scripts/history.py","line_end":41,"line_start":41},{"file":"scripts/publish.mjs","line_end":5,"line_start":5},{"file":"scripts/publish.mjs","line_end":44,"line_start":44},{"file":"scripts/publish.mjs","line_end":110,"line_start":110},{"file":"scripts/publish.mjs","line_end":201,"line_start":201},{"file":"SKILL.md","line_end":197,"line_start":197},{"file":"SKILL.md","line_end":205,"line_start":205},{"file":"SKILL.md","line_end":247,"line_start":247}]},{"factor":"env_access","evidence":[{"file":"scripts/history.py","line_end":55,"line_start":55},{"file":"scripts/history.py","line_end":65,"line_start":65},{"file":"scripts/publish.mjs","line_end":139,"line_start":139},{"file":"scripts/publish.mjs","line_end":140,"line_start":140},{"file":"scripts/publish.mjs","line_end":139,"line_start":139},{"file":"scripts/publish.mjs","line_end":140,"line_start":140}]}],"critical_findings":[],"high_findings":[],"medium_findings":[{"title":"Directory Packaging Can Follow Replaced Symlinks","locations":[{"file":"scripts/publish.mjs","line_end":111,"line_start":100},{"file":"scripts/publish.mjs","line_end":224,"line_start":197},{"file":"scripts/brewpage-run.sh","line_end":218,"line_start":203}],"confidence":0.93,"description":"Collection rejects symlinks using lstat, but zip later reopens the selected paths without a stable snapshot. A concurrent writer can replace a selected file or ancestor with a symlink to readable files outside the directory. Archive verification compares names, not source identity, so an unchanged innocuous filename can pass inspection and upload unintended data.","review_kind":"security","source_category":"semantic","source_severity":"medium","confidence_reasoning":"The code separates lstat validation from zip reads and verifies only archive names afterward. Exploitation requires concurrent write access to the source tree."}],"low_findings":[],"dangerous_patterns":[],"files_scanned":7,"total_lines":1319,"audit_model":"codex","audited_at":"2026-10-27T04:38:49.108+00:00","created_at":"2026-10-27T05:06:07.919399+00:00","static_findings":[{"id":"network:LICENSE:3:hardcoded-url","file":"LICENSE","pattern":"Hardcoded URL","snippet":"Copyright (c) 2025-2026 Maksim Kochetkov (kochetkov-ma), https://github.com/kochetkov-ma/claude-brew","category":"network","line_end":3,"severity":"low","line_start":3},{"id":"network:README.md:117:hardcoded-url","file":"README.md","pattern":"Hardcoded URL","snippet":"curl -X DELETE \"https://brewpage.app/api/{type}/{ns}/{id}\" -H \"X-Owner-Token: {token}\"","category":"network","line_end":117,"severity":"low","line_start":117},{"id":"network:README.md:127:hardcoded-url","file":"README.md","pattern":"Hardcoded URL","snippet":"claude plugin marketplace add https://github.com/kochetkov-ma/claude-brewcode","category":"network","line_end":127,"severity":"low","line_start":127},{"id":"sensitive:README.md:68:environment-file-access","file":"README.md","pattern":"Environment file access","snippet":"The directory bundle is **allowlisted**: it drops system files (`.DS_Store`, `__MACOSX/`, `Thumbs.db","category":"sensitive","line_end":68,"severity":"high","line_start":68},{"id":"sensitive:README.md:68:sqlite-database-file","file":"README.md","pattern":"SQLite database file","snippet":"The directory bundle is **allowlisted**: it drops system files (`.DS_Store`, `__MACOSX/`, `Thumbs.db","category":"sensitive","line_end":68,"severity":"medium","line_start":68},{"id":"blocker:README.md:91:system-reconnaissance","file":"README.md","pattern":"System reconnaissance","snippet":"An upload may return an existing publication. Keep that winner's identity, metadata and exact server","category":"blocker","line_end":91,"severity":"low","line_start":91},{"id":"external_commands:scripts/brewpage-lib.sh:51:ruby-shell-backtick-execution","file":"scripts/brewpage-lib.sh","pattern":"Ruby/shell backtick execution","snippet":"> Delete: `curl -s -X DELETE \"<Owner API>\" -H \"X-Owner-Token: TOKEN\"`; rows without one: `https://br","category":"external_commands","line_end":51,"severity":"medium","line_start":51},{"id":"external_commands:scripts/brewpage-lib.sh:52:ruby-shell-backtick-execution","file":"scripts/brewpage-lib.sh","pattern":"Ruby/shell backtick execution","snippet":"> Update a site (same URL; old publications answer 409, see above): `PUT /api/sites/<ns>/<id>` with ","category":"external_commands","line_end":52,"severity":"medium","line_start":52},{"id":"external_commands:scripts/brewpage-lib.sh:62:ruby-shell-backtick-execution","file":"scripts/brewpage-lib.sh","pattern":"Ruby/shell backtick execution","snippet":"printf '| %s | [%s](%s) | `%s` | %sd | %s | %s | `%s` |\\n' \\","category":"external_commands","line_end":62,"severity":"medium","line_start":62},{"id":"external_commands:scripts/brewpage-lib.sh:65:ruby-shell-backtick-execution","file":"scripts/brewpage-lib.sh","pattern":"Ruby/shell backtick execution","snippet":"printf '| %s | [%s](%s) | `%s` | %sd | %s |\\n' \\","category":"external_commands","line_end":65,"severity":"medium","line_start":65},{"id":"external_commands:scripts/brewpage-lib.sh:166:ruby-shell-backtick-execution","file":"scripts/brewpage-lib.sh","pattern":"Ruby/shell backtick execution","snippet":"# A function has its own positional parameters, so no `set --` juggling is needed.","category":"external_commands","line_end":166,"severity":"medium","line_start":166},{"id":"external_commands:scripts/brewpage-lib.sh:187:ruby-shell-backtick-execution","file":"scripts/brewpage-lib.sh","pattern":"Ruby/shell backtick execution","snippet":"# `site` is the only type whose response carries .fileCount.","category":"external_commands","line_end":187,"severity":"medium","line_start":187},{"id":"external_commands:scripts/brewpage-lib.sh:8:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"p=$(cd \"$1\" 2>/dev/null && pwd -P) || return 1","category":"external_commands","line_end":8,"severity":"medium","line_start":8},{"id":"external_commands:scripts/brewpage-lib.sh:9:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"[ -n \"${HOME:-}\" ] && [ \"$p\" = \"$(cd \"$HOME\" 2>/dev/null && pwd -P)\" ] && return 0","category":"external_commands","line_end":9,"severity":"medium","line_start":9},{"id":"external_commands:scripts/brewpage-lib.sh:10:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"[ -n \"${CLAUDE_CONFIG_DIR:-}\" ] && [ \"$p/.claude\" = \"$(cd \"$CLAUDE_CONFIG_DIR\" 2>/dev/null && pwd -P","category":"external_commands","line_end":10,"severity":"medium","line_start":10},{"id":"external_commands:scripts/brewpage-lib.sh:21:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"root=$(git rev-parse --show-toplevel 2>/dev/null) || root=\"\"","category":"external_commands","line_end":21,"severity":"medium","line_start":21},{"id":"external_commands:scripts/brewpage-lib.sh:32:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"d=$(dirname \"$d\")","category":"external_commands","line_end":32,"severity":"medium","line_start":32},{"id":"external_commands:scripts/brewpage-lib.sh:37:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"BP_ROOT=\"$(bp_project_root)\"","category":"external_commands","line_end":37,"severity":"medium","line_start":37},{"id":"external_commands:scripts/brewpage-lib.sh:47:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"python3 \"$(dirname \"${BASH_SOURCE[0]}\")/history.py\" init \"$BP_ROOT\" \"${HISTORY_FILE#\"$BP_ROOT/\"}\" <<","category":"external_commands","line_end":47,"severity":"medium","line_start":47},{"id":"external_commands:scripts/brewpage-lib.sh:63:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"\"$(date '+%Y-%m-%d %H:%M')\" \"$1\" \"$1\" \"$2\" \"$3\" \"$4\" \"$5\" \"${6:-}\" | python3 \"$(dirname \"${BASH_SOUR","category":"external_commands","line_end":63,"severity":"medium","line_start":63},{"id":"external_commands:scripts/brewpage-lib.sh:66:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"\"$(date '+%Y-%m-%d %H:%M')\" \"$1\" \"$1\" \"$2\" \"$3\" \"$4\" | python3 \"$(dirname \"${BASH_SOURCE[0]}\")/histo","category":"external_commands","line_end":66,"severity":"medium","line_start":66},{"id":"external_commands:scripts/brewpage-lib.sh:105:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"[ -n \"$(find \"$BP_RUN_DIR\" -maxdepth 0 -type d -perm 0700 -print)\" ] || {","category":"external_commands","line_end":105,"severity":"medium","line_start":105},{"id":"external_commands:scripts/brewpage-lib.sh:190:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"url=$(printf '%s' \"$response\" | jq -r '.link // empty')","category":"external_commands","line_end":190,"severity":"medium","line_start":190},{"id":"external_commands:scripts/brewpage-lib.sh:192:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"reason=$(bp_error_reason \"$response\")","category":"external_commands","line_end":192,"severity":"medium","line_start":192},{"id":"external_commands:scripts/brewpage-lib.sh:199:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"token=$(printf '%s' \"$response\" | jq -r '.ownerToken // empty')","category":"external_commands","line_end":199,"severity":"medium","line_start":199},{"id":"external_commands:scripts/brewpage-lib.sh:201:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"owner_api=$(printf '%s' \"$response\" | jq -r '.ownerLink // empty')","category":"external_commands","line_end":201,"severity":"medium","line_start":201},{"id":"external_commands:scripts/brewpage-lib.sh:202:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"identity=$(printf '%s' \"$response\" | jq -c '{id, namespace, type, routingCohort, deliveryMode, modeL","category":"external_commands","line_end":202,"severity":"medium","line_start":202},{"id":"external_commands:scripts/brewpage-lib.sh:204:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"fcount=$(printf '%s' \"$response\" | jq -r '.fileCount // \"?\"')","category":"external_commands","line_end":204,"severity":"medium","line_start":204},{"id":"external_commands:scripts/brewpage-lib.sh:246:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"digest=$(python3 - \"$zip\" <<'PY'","category":"external_commands","line_end":248,"severity":"medium","line_start":246},{"id":"external_commands:scripts/brewpage-lib.sh:265:shell-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Shell command substitution","snippet":"ENTRY=$(printf '%s\\n' \"$manifest\" | sed -n 's/^ENTRY: //p')","category":"external_commands","line_end":265,"severity":"medium","line_start":265},{"id":"external_commands:scripts/brewpage-lib.sh:62:template-literal-with-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Template literal with command substitution","snippet":"printf '| %s | [%s](%s) | `%s` | %sd | %s | %s | `%s` |\\n' \\","category":"external_commands","line_end":65,"severity":"medium","line_start":62},{"id":"external_commands:scripts/brewpage-lib.sh:65:template-literal-with-command-substitution","file":"scripts/brewpage-lib.sh","pattern":"Template literal with command substitution","snippet":"printf '| %s | [%s](%s) | `%s` | %sd | %s |\\n' \\","category":"external_commands","line_end":166,"severity":"medium","line_start":65},{"id":"network:scripts/brewpage-lib.sh:51:hardcoded-url","file":"scripts/brewpage-lib.sh","pattern":"Hardcoded URL","snippet":"> Delete: `curl -s -X DELETE \"<Owner API>\" -H \"X-Owner-Token: TOKEN\"`; rows without one: `https://br","category":"network","line_end":51,"severity":"low","line_start":51},{"id":"filesystem:scripts/brewpage-lib.sh:84:path-traversal-sequence","file":"scripts/brewpage-lib.sh","pattern":"Path traversal sequence","snippet":"*[!ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._/-]* | */../* | ../* | */..) echo","category":"filesystem","line_end":84,"severity":"high","line_start":84},{"id":"filesystem:scripts/brewpage-lib.sh:10:hidden-file-access","file":"scripts/brewpage-lib.sh","pattern":"Hidden file access","snippet":"[ -n \"${CLAUDE_CONFIG_DIR:-}\" ] && [ \"$p/.claude\" = \"$(cd \"$CLAUDE_CONFIG_DIR\" 2>/dev/null && pwd -P","category":"filesystem","line_end":10,"severity":"medium","line_start":10},{"id":"filesystem:scripts/brewpage-lib.sh:14:hidden-file-access","file":"scripts/brewpage-lib.sh","pattern":"Hidden file access","snippet":"# walk for .git/.claude (profile parents skipped) -> PWD. The history file must not follow a nested ","category":"filesystem","line_end":14,"severity":"medium","line_start":14},{"id":"filesystem:scripts/brewpage-lib.sh:28:hidden-file-access","file":"scripts/brewpage-lib.sh","pattern":"Hidden file access","snippet":"if [ -d \"$d/.git\" ] || { [ -d \"$d/.claude\" ] && ! bp_is_profile_parent \"$d\"; }; then","category":"filesystem","line_end":28,"severity":"medium","line_start":28},{"id":"filesystem:scripts/brewpage-lib.sh:38:hidden-file-access","file":"scripts/brewpage-lib.sh","pattern":"Hidden file access","snippet":"HISTORY_FILE=\"$BP_ROOT/.claude/brewpage-history.md\"","category":"filesystem","line_end":38,"severity":"medium","line_start":38},{"id":"filesystem:scripts/brewpage-lib.sh:8:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"p=$(cd \"$1\" 2>/dev/null && pwd -P) || return 1","category":"filesystem","line_end":8,"severity":"low","line_start":8},{"id":"filesystem:scripts/brewpage-lib.sh:9:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"[ -n \"${HOME:-}\" ] && [ \"$p\" = \"$(cd \"$HOME\" 2>/dev/null && pwd -P)\" ] && return 0","category":"filesystem","line_end":9,"severity":"low","line_start":9},{"id":"filesystem:scripts/brewpage-lib.sh:10:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"[ -n \"${CLAUDE_CONFIG_DIR:-}\" ] && [ \"$p/.claude\" = \"$(cd \"$CLAUDE_CONFIG_DIR\" 2>/dev/null && pwd -P","category":"filesystem","line_end":10,"severity":"low","line_start":10},{"id":"filesystem:scripts/brewpage-lib.sh:21:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"root=$(git rev-parse --show-toplevel 2>/dev/null) || root=\"\"","category":"filesystem","line_end":21,"severity":"low","line_start":21},{"id":"filesystem:scripts/brewpage-lib.sh:46:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"command -v python3 >/dev/null || { echo 'FAILED: python3 required for secure history'; return 1; }","category":"filesystem","line_end":46,"severity":"low","line_start":46},{"id":"filesystem:scripts/brewpage-lib.sh:132:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"command -v jq >/dev/null || { echo \"FAILED: jq required\"; return 1; }","category":"filesystem","line_end":132,"severity":"low","line_start":132},{"id":"filesystem:scripts/brewpage-lib.sh:133:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"jq -ner --rawfile password \"$1\" \"$BP_PW_FILTER\" >/dev/null 2>&1 ||","category":"filesystem","line_end":133,"severity":"low","line_start":133},{"id":"filesystem:scripts/brewpage-lib.sh:148:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"command -v jq >/dev/null || { echo \"FAILED: jq required\"; return 1; }","category":"filesystem","line_end":148,"severity":"low","line_start":148},{"id":"filesystem:scripts/brewpage-lib.sh:160:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"(umask 077; jq -ner --rawfile password \"$password_file\" \"$BP_PW_FILTER\" > \"$BP_HEADER_FILE\") 2>/dev/","category":"filesystem","line_end":160,"severity":"low","line_start":160},{"id":"filesystem:scripts/brewpage-lib.sh:183:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"| \"HTTP \\(.status): \\((.message // .error // \"\") | tostring | explode | map(if . < 32 then 32 else .","category":"filesystem","line_end":183,"severity":"low","line_start":183},{"id":"filesystem:scripts/brewpage-lib.sh:196:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"if printf '%s' \"$response\" | jq -e '.result.action == \"merged\" and .result.extras.isOwner == false' ","category":"filesystem","line_end":196,"severity":"low","line_start":196},{"id":"filesystem:scripts/brewpage-lib.sh:212:standard-device-file-access","file":"scripts/brewpage-lib.sh","pattern":"Standard device file access","snippet":"printf '%s' \"$response\" | jq -r '.notices[]?.message // empty | \"NOTICE: \" + (tostring | explode | m","category":"filesystem","line_end":212,"severity":"low","line_start":212},{"id":"filesystem:scripts/brewpage-lib.sh:93:temp-file-creation","file":"scripts/brewpage-lib.sh","pattern":"Temp file creation","snippet":"(umask 077; mktemp -d \"$BP_TMPDIR/brewpage.XXXXXX\")","category":"filesystem","line_end":93,"severity":"low","line_start":93},{"id":"filesystem:scripts/brewpage-lib.sh:226:python-file-write-append","file":"scripts/brewpage-lib.sh","pattern":"Python file write/append","snippet":"with os.fdopen(fd, 'wb') as outgoing:","category":"filesystem","line_end":226,"severity":"medium","line_start":226},{"id":"blocker:scripts/brewpage-lib.sh:100:system-reconnaissance","file":"scripts/brewpage-lib.sh","pattern":"System reconnaissance","snippet":"*) echo \"FAILED: invalid publish run id\"; return 1 ;;","category":"blocker","line_end":100,"severity":"low","line_start":100},{"id":"blocker:scripts/brewpage-lib.sh:102:system-reconnaissance","file":"scripts/brewpage-lib.sh","pattern":"System reconnaissance","snippet":"case \"$id\" in *[!ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.]*) echo \"FAILED: in","category":"blocker","line_end":102,"severity":"low","line_start":102},{"id":"blocker:scripts/brewpage-lib.sh:125:system-reconnaissance","file":"scripts/brewpage-lib.sh","pattern":"System reconnaissance","snippet":"then \"X-Password: \" + $p else error(\"invalid password\") end'","category":"blocker","line_end":125,"severity":"low","line_start":125},{"id":"blocker:scripts/brewpage-lib.sh:127:system-reconnaissance","file":"scripts/brewpage-lib.sh","pattern":"System reconnaissance","snippet":"# {password_file} slot: absolute path to a readable non-symlink regular file with a valid password.","category":"blocker","line_end":127,"severity":"low","line_start":127},{"id":"blocker:scripts/brewpage-lib.sh:251:system-reconnaissance","file":"scripts/brewpage-lib.sh","pattern":"System reconnaissance","snippet":"if not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid() or info.st_nlink != 1:","category":"blocker","line_end":251,"severity":"low","line_start":251},{"id":"external_commands:scripts/brewpage-run.sh:6:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"HERE=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"","category":"external_commands","line_end":6,"severity":"medium","line_start":6},{"id":"external_commands:scripts/brewpage-run.sh:25:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"BP_FRESH=$(bp_prepare) || die \"cannot allocate a private run directory\"","category":"external_commands","line_end":25,"severity":"medium","line_start":25},{"id":"external_commands:scripts/brewpage-run.sh:47:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"abs=$(cd \"$p\" && pwd -P)","category":"external_commands","line_end":47,"severity":"medium","line_start":47},{"id":"external_commands:scripts/brewpage-run.sh:49:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"html_files=$(cd \"$abs\" && find . -type f \\( -name '*.html' -o -name '*.htm' \\) | sed 's|^\\./||' | LC","category":"external_commands","line_end":49,"severity":"medium","line_start":49},{"id":"external_commands:scripts/brewpage-run.sh:52:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"entry=$(printf '%s\\n' \"$html_files\" | awk -F/ 'NF { print NF \"\\t\" $0 }' | LC_ALL=C sort -s -n -k1,1 ","category":"external_commands","line_end":52,"severity":"medium","line_start":52},{"id":"external_commands:scripts/brewpage-run.sh:55:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"\"$(find \"$abs\" -type f | wc -l | tr -d ' ')\" \"$(printf '%s' \"$html_files\" | grep -c . || true)\" \\","category":"external_commands","line_end":55,"severity":"medium","line_start":55},{"id":"external_commands:scripts/brewpage-run.sh:56:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"\"$(du -sk \"$abs\" | cut -f1)\" \"${entry:-none}\"","category":"external_commands","line_end":56,"severity":"medium","line_start":56},{"id":"external_commands:scripts/brewpage-run.sh:58:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"abs=\"$(cd \"$(dirname \"$p\")\" && pwd -P)/$(basename \"$p\")\"","category":"external_commands","line_end":58,"severity":"medium","line_start":58},{"id":"external_commands:scripts/brewpage-run.sh:61:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"printf 'TYPE: %s\\nPATH: %s\\nBYTES: %s\\nMIME: %s\\n' \"$type\" \"$abs\" \"$(wc -c < \"$abs\" | tr -d ' ')\" \\","category":"external_commands","line_end":61,"severity":"medium","line_start":61},{"id":"external_commands:scripts/brewpage-run.sh:62:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"\"$(file --mime-type -b -- \"$abs\" 2>/dev/null || echo unknown)\"","category":"external_commands","line_end":62,"severity":"medium","line_start":62},{"id":"external_commands:scripts/brewpage-run.sh:73:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"body=$(curl -s -A \"ClaudeCode/1.0\" https://brewpage.app/api/limits) || die \"limits request failed\"","category":"external_commands","line_end":73,"severity":"medium","line_start":73},{"id":"external_commands:scripts/brewpage-run.sh:103:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"printf 'RUN: %s\\nBYTES: %s\\n' \"$run\" \"$(wc -c < \"$file\" | tr -d ' ')\"","category":"external_commands","line_end":103,"severity":"medium","line_start":103},{"id":"external_commands:scripts/brewpage-run.sh:127:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"--text) need \"$#\" \"$1\"; text=\"$2\"; have_text=1; sources=$((sources + 1)); shift 2 ;;","category":"external_commands","line_end":127,"severity":"medium","line_start":127},{"id":"external_commands:scripts/brewpage-run.sh:128:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"--run) need \"$#\" \"$1\"; run=\"$2\"; sources=$((sources + 1)); shift 2 ;;","category":"external_commands","line_end":128,"severity":"medium","line_start":128},{"id":"external_commands:scripts/brewpage-run.sh:129:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"--path) need \"$#\" \"$1\"; path=\"$2\"; sources=$((sources + 1)); shift 2 ;;","category":"external_commands","line_end":129,"severity":"medium","line_start":129},{"id":"external_commands:scripts/brewpage-run.sh:188:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"response=$(bp_post \"https://brewpage.app/api/html?ns=$NS&ttl=$DAYS&format=markdown\" \\","category":"external_commands","line_end":189,"severity":"medium","line_start":188},{"id":"external_commands:scripts/brewpage-run.sh:194:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"response=$(bp_post \"https://brewpage.app/api/json?ns=$NS&ttl=$DAYS\" \\","category":"external_commands","line_end":195,"severity":"medium","line_start":194},{"id":"external_commands:scripts/brewpage-run.sh:199:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"response=$(bp_post \"https://brewpage.app/api/files?ns=$NS&ttl=$DAYS\" -F \"$(form_file file \"$path\")\")","category":"external_commands","line_end":199,"severity":"medium","line_start":199},{"id":"external_commands:scripts/brewpage-run.sh:205:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"manifest=$(umask 077; node \"$HERE/publish.mjs\" pack --dir \"$path\" --out \"$archive\" ${ENTRY:+--entry ","category":"external_commands","line_end":205,"severity":"medium","line_start":205},{"id":"external_commands:scripts/brewpage-run.sh:212:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"manifest=$(node \"$HERE/publish.mjs\" inspect --zip \"$archive\" ${ENTRY:+--entry \"$ENTRY\"}) || rc=$?","category":"external_commands","line_end":212,"severity":"medium","line_start":212},{"id":"external_commands:scripts/brewpage-run.sh:216:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"response=$(bp_post \"https://brewpage.app/api/sites?ns=$NS&ttl=$DAYS&entry=$(jq -nr --arg e \"$ENTRY\" ","category":"external_commands","line_end":216,"severity":"medium","line_start":216},{"id":"external_commands:scripts/brewpage-run.sh:217:shell-command-substitution","file":"scripts/brewpage-run.sh","pattern":"Shell command substitution","snippet":"-H \"User-Agent: ClaudeCode/1.0\" -F \"$(form_file archive \"$archive\")\") || die \"upload request failed\"","category":"external_commands","line_end":217,"severity":"medium","line_start":217},{"id":"network:scripts/brewpage-run.sh:73:hardcoded-url","file":"scripts/brewpage-run.sh","pattern":"Hardcoded URL","snippet":"body=$(curl -s -A \"ClaudeCode/1.0\" https://brewpage.app/api/limits) || die \"limits request failed\"","category":"network","line_end":73,"severity":"low","line_start":73},{"id":"network:scripts/brewpage-run.sh:188:hardcoded-url","file":"scripts/brewpage-run.sh","pattern":"Hardcoded URL","snippet":"response=$(bp_post \"https://brewpage.app/api/html?ns=$NS&ttl=$DAYS&format=markdown\" \\","category":"network","line_end":188,"severity":"low","line_start":188},{"id":"network:scripts/brewpage-run.sh:194:hardcoded-url","file":"scripts/brewpage-run.sh","pattern":"Hardcoded URL","snippet":"response=$(bp_post \"https://brewpage.app/api/json?ns=$NS&ttl=$DAYS\" \\","category":"network","line_end":194,"severity":"low","line_start":194},{"id":"network:scripts/brewpage-run.sh:199:hardcoded-url","file":"scripts/brewpage-run.sh","pattern":"Hardcoded URL","snippet":"response=$(bp_post \"https://brewpage.app/api/files?ns=$NS&ttl=$DAYS\" -F \"$(form_file file \"$path\")\")","category":"network","line_end":199,"severity":"low","line_start":199},{"id":"network:scripts/brewpage-run.sh:216:hardcoded-url","file":"scripts/brewpage-run.sh","pattern":"Hardcoded URL","snippet":"response=$(bp_post \"https://brewpage.app/api/sites?ns=$NS&ttl=$DAYS&entry=$(jq -nr --arg e \"$ENTRY\" ","category":"network","line_end":216,"severity":"low","line_start":216},{"id":"filesystem:scripts/brewpage-run.sh:62:standard-device-file-access","file":"scripts/brewpage-run.sh","pattern":"Standard device file access","snippet":"\"$(file --mime-type -b -- \"$abs\" 2>/dev/null || echo unknown)\"","category":"filesystem","line_end":62,"severity":"low","line_start":62},{"id":"filesystem:scripts/brewpage-run.sh:71:standard-device-file-access","file":"scripts/brewpage-run.sh","pattern":"Standard device file access","snippet":"command -v jq >/dev/null || die \"jq required\"","category":"filesystem","line_end":71,"severity":"low","line_start":71},{"id":"filesystem:scripts/brewpage-run.sh:77:standard-device-file-access","file":"scripts/brewpage-run.sh","pattern":"Standard device file access","snippet":"\"PATH_MAX_CHARS: \\($s.maxPathChars)\", \"PATH_MAX_BYTES: \\($s.maxPathBytes)\", \"NAME_MAX_BYTES: \\($s.ma","category":"filesystem","line_end":77,"severity":"low","line_start":77},{"id":"filesystem:scripts/brewpage-run.sh:136:standard-device-file-access","file":"scripts/brewpage-run.sh","pattern":"Standard device file access","snippet":"command -v python3 >/dev/null || die \"python3 required for secure history\"","category":"filesystem","line_end":136,"severity":"low","line_start":136},{"id":"filesystem:scripts/brewpage-run.sh:193:standard-device-file-access","file":"scripts/brewpage-run.sh","pattern":"Standard device file access","snippet":"jq empty \"$content\" 2>/dev/null || die \"payload is not valid JSON\"","category":"filesystem","line_end":193,"severity":"low","line_start":193},{"id":"blocker:scripts/brewpage-run.sh:160:system-reconnaissance","file":"scripts/brewpage-run.sh","pattern":"System reconnaissance","snippet":"local id pwmode=none","category":"blocker","line_end":160,"severity":"low","line_start":160},{"id":"blocker:scripts/brewpage-run.sh:178:system-reconnaissance","file":"scripts/brewpage-run.sh","pattern":"System reconnaissance","snippet":"[ -f \"$content\" ] || die \"run $id has no staged text\"","category":"blocker","line_end":178,"severity":"low","line_start":178},{"id":"blocker:scripts/brewpage-run.sh:193:system-reconnaissance","file":"scripts/brewpage-run.sh","pattern":"System reconnaissance","snippet":"jq empty \"$content\" 2>/dev/null || die \"payload is not valid JSON\"","category":"blocker","line_end":193,"severity":"low","line_start":193},{"id":"external_commands:scripts/history.py:55:python-subprocess-run","file":"scripts/history.py","pattern":"Python subprocess.run","snippet":"result = subprocess.run(['git', '-C', root] + arguments, stdout=subprocess.PIPE, stderr=subprocess.P","category":"external_commands","line_end":55,"severity":"high","line_start":55},{"id":"filesystem:scripts/history.py:33:python-file-write-append","file":"scripts/history.py","pattern":"Python file write/append","snippet":"with os.fdopen(fd, 'wb') as output:","category":"filesystem","line_end":33,"severity":"medium","line_start":33},{"id":"filesystem:scripts/history.py:41:python-os-file-operations","file":"scripts/history.py","pattern":"Python os file operations","snippet":"os.unlink(temporary, dir_fd=directory)","category":"filesystem","line_end":41,"severity":"medium","line_start":41},{"id":"env_access:scripts/history.py:55:python-environment-access","file":"scripts/history.py","pattern":"Python environment access","snippet":"result = subprocess.run(['git', '-C', root] + arguments, stdout=subprocess.PIPE, stderr=subprocess.P","category":"env_access","line_end":55,"severity":"low","line_start":55},{"id":"env_access:scripts/history.py:65:python-environment-access","file":"scripts/history.py","pattern":"Python environment access","snippet":"if os.path.lexists(os.path.join(parent, '.git')) or os.environ.get('GIT_DIR'):","category":"env_access","line_end":65,"severity":"low","line_start":65},{"id":"blocker:scripts/history.py:23:system-reconnaissance","file":"scripts/history.py","pattern":"System reconnaissance","snippet":"if not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid() or info.st_nlink != 1:","category":"blocker","line_end":23,"severity":"low","line_start":23},{"id":"blocker:scripts/history.py:82:system-reconnaissance","file":"scripts/history.py","pattern":"System reconnaissance","snippet":"raise ValueError('invalid history operation or destination')","category":"blocker","line_end":82,"severity":"low","line_start":82},{"id":"blocker:scripts/history.py:87:system-reconnaissance","file":"scripts/history.py","pattern":"System reconnaissance","snippet":"if info.st_uid != os.getuid() or info.st_mode & 0o022:","category":"blocker","line_end":87,"severity":"low","line_start":87},{"id":"blocker:scripts/history.py:99:system-reconnaissance","file":"scripts/history.py","pattern":"System reconnaissance","snippet":"if info.st_uid != os.getuid() or info.st_mode & 0o022:","category":"blocker","line_end":99,"severity":"low","line_start":99},{"id":"external_commands:scripts/publish.mjs:4:node-js-child-process-module","file":"scripts/publish.mjs","pattern":"Node.js child_process module","snippet":"import { spawnSync } from 'node:child_process';","category":"external_commands","line_end":4,"severity":"high","line_start":4},{"id":"external_commands:scripts/publish.mjs:153:synchronous-spawn","file":"scripts/publish.mjs","pattern":"Synchronous spawn","snippet":"const r = spawnSync('unzip', [flag, zipPath], { encoding: 'utf8', maxBuffer: LIST_MAX_BUFFER, timeou","category":"external_commands","line_end":153,"severity":"high","line_start":153},{"id":"external_commands:scripts/publish.mjs:205:synchronous-spawn","file":"scripts/publish.mjs","pattern":"Synchronous spawn","snippet":"const z = spawnSync('zip', ['-q', '-X', '-@', out], {","category":"external_commands","line_end":205,"severity":"high","line_start":205},{"id":"external_commands:scripts/publish.mjs:2:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"/** Offline SITE bundle build/verify: `pack --dir <abs-dir> --out <abs.zip> [--entry <name>]` | `ins","category":"external_commands","line_end":2,"severity":"medium","line_start":2},{"id":"external_commands:scripts/publish.mjs:26:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"/** True when a dot-named segment, outside the root `.well-known/`, keeps the path out of a site. */","category":"external_commands","line_end":26,"severity":"medium","line_start":26},{"id":"external_commands:scripts/publish.mjs:29:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"/** Extension-less files ship only inside the root `.well-known/` (e.g. `apple-app-site-association`","category":"external_commands","line_end":29,"severity":"medium","line_start":29},{"id":"external_commands:scripts/publish.mjs:30:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"const isWellKnownBare = (rel) => rel.startsWith(`${TRUSTED_DOT_DIR}/`) && extname(rel) === '';","category":"external_commands","line_end":30,"severity":"medium","line_start":30},{"id":"external_commands:scripts/publish.mjs:37:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"/** `unzip` stdout cap: Node's 1 MB default kills `-Z1` on a valid site with >1 MB of names (ENOBUFS","category":"external_commands","line_end":37,"severity":"medium","line_start":37},{"id":"external_commands:scripts/publish.mjs:71:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"console.log(`FAILED: ${msg}`);","category":"external_commands","line_end":71,"severity":"medium","line_start":71},{"id":"external_commands:scripts/publish.mjs:81:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"if (!k.startsWith('--') || v === undefined) die(`bad argument near \"${k}\"`);","category":"external_commands","line_end":81,"severity":"medium","line_start":81},{"id":"external_commands:scripts/publish.mjs:90:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"die(`entry \"${entry}\" is not a plain relative file name`);","category":"external_commands","line_end":90,"severity":"medium","line_start":90},{"id":"external_commands:scripts/publish.mjs:120:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"if (!names.includes(requested)) die(`entry \"${requested}\" is not in the archive`);","category":"external_commands","line_end":120,"severity":"medium","line_start":120},{"id":"external_commands:scripts/publish.mjs:132:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"/** True when `p` is an executable regular file. */","category":"external_commands","line_end":132,"severity":"medium","line_start":132},{"id":"external_commands:scripts/publish.mjs:141:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"if (!dirs.some((dir) => exts.some((ext) => isExecutable(join(dir, bin + ext))))) die(`${bin} require","category":"external_commands","line_end":141,"severity":"medium","line_start":141},{"id":"external_commands:scripts/publish.mjs:146:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"if (r.status !== 0 && r.status !== null) return `${bin} exited ${r.status}`;","category":"external_commands","line_end":146,"severity":"medium","line_start":146},{"id":"external_commands:scripts/publish.mjs:147:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"if (r.error) return `${bin} failed (${r.error.code || r.error.message})`;","category":"external_commands","line_end":147,"severity":"medium","line_start":147},{"id":"external_commands:scripts/publish.mjs:148:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"return r.signal ? `${bin} killed by ${r.signal}` : '';","category":"external_commands","line_end":148,"severity":"medium","line_start":148},{"id":"external_commands:scripts/publish.mjs:151:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"/** One `unzip` run: { out }, or { why } telling a bad archive (non-zero exit) from a check that nev","category":"external_commands","line_end":151,"severity":"medium","line_start":151},{"id":"external_commands:scripts/publish.mjs:156:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"return why ? { why: `archive could not be checked: ${why}` } : { out: r.stdout || '' };","category":"external_commands","line_end":156,"severity":"medium","line_start":156},{"id":"external_commands:scripts/publish.mjs:159:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"/** `unzip -tq` then `unzip -Z1`: { names } sorted, directory entries dropped, or { why }. */","category":"external_commands","line_end":159,"severity":"medium","line_start":159},{"id":"external_commands:scripts/publish.mjs:173:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"console.log(`ARCHIVE MANIFEST — ${basename(zipPath)}`);","category":"external_commands","line_end":173,"severity":"medium","line_start":173},{"id":"external_commands:scripts/publish.mjs:174:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"for (const n of names) console.log(`  ${n}`);","category":"external_commands","line_end":174,"severity":"medium","line_start":174},{"id":"external_commands:scripts/publish.mjs:175:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"console.log(`FILES: ${names.length}`);","category":"external_commands","line_end":175,"severity":"medium","line_start":175},{"id":"external_commands:scripts/publish.mjs:176:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"console.log(`BYTES: ${bytes}`);","category":"external_commands","line_end":176,"severity":"medium","line_start":176},{"id":"external_commands:scripts/publish.mjs:177:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"console.log(`ENTRY: ${entry}`);","category":"external_commands","line_end":177,"severity":"medium","line_start":177},{"id":"external_commands:scripts/publish.mjs:178:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"console.log(`SKIPPED: ${skipped.length}${skipped.length ? ` (${skipped.join(', ')})` : ''}`);","category":"external_commands","line_end":178,"severity":"medium","line_start":178},{"id":"external_commands:scripts/publish.mjs:179:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"if (unexpected.length) console.log(`UNEXPECTED: ${unexpected.join(', ')}`);","category":"external_commands","line_end":179,"severity":"medium","line_start":179},{"id":"external_commands:scripts/publish.mjs:181:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"if (flagged.length) console.log(`SENSITIVE: ${flagged.join(', ')}`);","category":"external_commands","line_end":181,"severity":"medium","line_start":181},{"id":"external_commands:scripts/publish.mjs:183:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"console.log(`RESULT: ${confirm ? 'confirm' : 'ok'}`);","category":"external_commands","line_end":183,"severity":"medium","line_start":183},{"id":"external_commands:scripts/publish.mjs:192:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"if (!existsSync(dir) || !statSync(dir).isDirectory()) die(`not a directory: ${dir}`);","category":"external_commands","line_end":192,"severity":"medium","line_start":192},{"id":"external_commands:scripts/publish.mjs:198:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"if (kept.length === 0) die(`no publishable files under ${dir} after the allowlist filter`);","category":"external_commands","line_end":198,"severity":"medium","line_start":198},{"id":"external_commands:scripts/publish.mjs:201:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"// BD-N03: `mktemp` leaves a 0-byte file that Info-ZIP reads as a corrupt","category":"external_commands","line_end":201,"severity":"medium","line_start":201},{"id":"external_commands:scripts/publish.mjs:206:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"cwd: dir, input: `${kept.join('\\n')}\\n`, encoding: 'utf8',","category":"external_commands","line_end":206,"severity":"medium","line_start":206},{"id":"external_commands:scripts/publish.mjs:212:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"die(`${zipWhy} — no archive produced`);","category":"external_commands","line_end":212,"severity":"medium","line_start":212},{"id":"external_commands:scripts/publish.mjs:224:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"die(`archive contents differ from the selected file set (in archive: ${listed.length}, selected: ${k","category":"external_commands","line_end":224,"severity":"medium","line_start":224},{"id":"external_commands:scripts/publish.mjs:232:ruby-shell-backtick-execution","file":"scripts/publish.mjs","pattern":"Ruby/shell backtick execution","snippet":"if (!existsSync(zipPath) || !statSync(zipPath).isFile()) die(`not a file: ${zipPath}`);","category":"external_commands","line_end":232,"severity":"medium","line_start":232},{"id":"filesystem:scripts/publish.mjs:5:synchronous-file-operations","file":"scripts/publish.mjs","pattern":"Synchronous file operations","snippet":"import { readdirSync, statSync, lstatSync, existsSync, rmSync, accessSync, constants, readFileSync }","category":"filesystem","line_end":5,"severity":"medium","line_start":5},{"id":"filesystem:scripts/publish.mjs:44:synchronous-file-operations","file":"scripts/publish.mjs","pattern":"Synchronous file operations","snippet":"const data = readFileSync(zipPath);","category":"filesystem","line_end":44,"severity":"medium","line_start":44},{"id":"filesystem:scripts/publish.mjs:110:hidden-file-access","file":"scripts/publish.mjs","pattern":"Hidden file access","snippet":"if (/[\\r\\n]/.test(rel)) { skipped.push(rel); continue; }","category":"filesystem","line_end":110,"severity":"medium","line_start":110},{"id":"filesystem:scripts/publish.mjs:201:temp-file-creation","file":"scripts/publish.mjs","pattern":"Temp file creation","snippet":"// BD-N03: `mktemp` leaves a 0-byte file that Info-ZIP reads as a corrupt","category":"filesystem","line_end":201,"severity":"low","line_start":201},{"id":"env_access:scripts/publish.mjs:139:environment-variable-access-dot-notation","file":"scripts/publish.mjs","pattern":"Environment variable access (dot notation)","snippet":"const exts = process.platform === 'win32' ? ['', ...(process.env.PATHEXT || '.EXE;.CMD;.BAT').split(","category":"env_access","line_end":139,"severity":"low","line_start":139},{"id":"env_access:scripts/publish.mjs:140:environment-variable-access-dot-notation","file":"scripts/publish.mjs","pattern":"Environment variable access (dot notation)","snippet":"const dirs = (process.env.PATH || '').split(delimiter).filter(Boolean);","category":"env_access","line_end":140,"severity":"low","line_start":140},{"id":"env_access:scripts/publish.mjs:139:environment-variable-object","file":"scripts/publish.mjs","pattern":"Environment variable object","snippet":"const exts = process.platform === 'win32' ? ['', ...(process.env.PATHEXT || '.EXE;.CMD;.BAT').split(","category":"env_access","line_end":139,"severity":"low","line_start":139},{"id":"env_access:scripts/publish.mjs:140:environment-variable-object","file":"scripts/publish.mjs","pattern":"Environment variable object","snippet":"const dirs = (process.env.PATH || '').split(delimiter).filter(Boolean);","category":"env_access","line_end":140,"severity":"low","line_start":140},{"id":"sensitive:scripts/publish.mjs:33:ssh-private-key-file","file":"scripts/publish.mjs","pattern":"SSH private key file","snippet":"const SENSITIVE_RE = /(^|\\/)(\\.env|id_rsa|id_ed25519)|secret|credential|password|token|\\.(pem|key|p1","category":"sensitive","line_end":33,"severity":"critical","line_start":33},{"id":"sensitive:scripts/publish.mjs:33:environment-file-access","file":"scripts/publish.mjs","pattern":"Environment file access","snippet":"const SENSITIVE_RE = /(^|\\/)(\\.env|id_rsa|id_ed25519)|secret|credential|password|token|\\.(pem|key|p1","category":"sensitive","line_end":33,"severity":"high","line_start":33},{"id":"sensitive:scripts/publish.mjs:139:environment-file-access","file":"scripts/publish.mjs","pattern":"Environment file access","snippet":"const exts = process.platform === 'win32' ? ['', ...(process.env.PATHEXT || '.EXE;.CMD;.BAT').split(","category":"sensitive","line_end":139,"severity":"high","line_start":139},{"id":"sensitive:scripts/publish.mjs:140:environment-file-access","file":"scripts/publish.mjs","pattern":"Environment file access","snippet":"const dirs = (process.env.PATH || '').split(delimiter).filter(Boolean);","category":"sensitive","line_end":140,"severity":"high","line_start":140},{"id":"sensitive:scripts/publish.mjs:24:sqlite-database-file","file":"scripts/publish.mjs","pattern":"SQLite database file","snippet":"const SYSTEM_RE = /^(__MACOSX|\\.Spotlight-V100|\\.Trashes|\\.fseventsd)\\/|(^|\\/)(\\.DS_Store|Thumbs\\.db","category":"sensitive","line_end":24,"severity":"medium","line_start":24},{"id":"blocker:scripts/publish.mjs:37:system-reconnaissance","file":"scripts/publish.mjs","pattern":"System reconnaissance","snippet":"/** `unzip` stdout cap: Node's 1 MB default kills `-Z1` on a valid site with >1 MB of names (ENOBUFS","category":"blocker","line_end":37,"severity":"low","line_start":37},{"id":"blocker:scripts/publish.mjs:48:system-reconnaissance","file":"scripts/publish.mjs","pattern":"System reconnaissance","snippet":"if (end < floor) return 'archive has no valid central directory';","category":"blocker","line_end":48,"severity":"low","line_start":48},{"id":"external_commands:SKILL.md:18:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Every shell step is ONE command of the exact shape in the fences below: the single `allowed-tools`","category":"external_commands","line_end":18,"severity":"medium","line_start":18},{"id":"external_commands:SKILL.md:21:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Fill each `'{...}'` slot with the raw value and write every `'` inside it as `'\\''`; nothing else ","category":"external_commands","line_end":21,"severity":"medium","line_start":21},{"id":"external_commands:SKILL.md:22:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"escaped. Never wrap a value in double quotes. An empty slot stays `''` (= omitted).","category":"external_commands","line_end":22,"severity":"medium","line_start":22},{"id":"external_commands:SKILL.md:23:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Never quote the script path, prefix or chain the command (`cd`, `VAR=...`, `;`, `&&`, `|`, `$(...)","category":"external_commands","line_end":23,"severity":"medium","line_start":23},{"id":"external_commands:SKILL.md:26:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"through `stage` (Step 6).","category":"external_commands","line_end":26,"severity":"medium","line_start":26},{"id":"external_commands:SKILL.md:27:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- The script prints one decisive line: `OK {url}` or `OK {url} | Files: N` (then any `NOTICE: ...` l","category":"external_commands","line_end":27,"severity":"medium","line_start":27},{"id":"external_commands:SKILL.md:28:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"what the server skipped or adjusted), `CONFIRM: ...` (exit 2, nothing uploaded) or `FAILED: ...` (se","category":"external_commands","line_end":28,"severity":"medium","line_start":28},{"id":"external_commands:SKILL.md:35:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Extract from `$ARGUMENTS`:","category":"external_commands","line_end":35,"severity":"medium","line_start":35},{"id":"external_commands:SKILL.md:36:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- `--ttl N` → TTL in days (default: `15`)","category":"external_commands","line_end":36,"severity":"medium","line_start":36},{"id":"external_commands:SKILL.md:37:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- `--entry <filename>` → entry file for SITE uploads (default: auto-detect)","category":"external_commands","line_end":37,"severity":"medium","line_start":37},{"id":"external_commands:SKILL.md:38:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- `--delivery-mode path|subdomain` → optional creation header; omitted means server defaults. Reject","category":"external_commands","line_end":38,"severity":"medium","line_start":38},{"id":"external_commands:SKILL.md:39:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Remaining text → `content_arg`: a path, or the exact text to publish. Fired by Claude mid-task, th","category":"external_commands","line_end":39,"severity":"medium","line_start":39},{"id":"external_commands:SKILL.md:44:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"A `content_arg` that names a path -> probe it (read-only; prints `TYPE:`, the absolute `PATH:` to us","category":"external_commands","line_end":44,"severity":"medium","line_start":44},{"id":"external_commands:SKILL.md:47:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":49,"severity":"medium","line_start":47},{"id":"external_commands:SKILL.md:49:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":51,"severity":"medium","line_start":49},{"id":"external_commands:SKILL.md:51:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| Input | Type | `publish` type + source | API |","category":"external_commands","line_end":53,"severity":"medium","line_start":51},{"id":"external_commands:SKILL.md:53:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| `TYPE: site-dir` | SITE | `site-dir` + `--path` | `POST /api/sites` (ZIP built from the dir — prim","category":"external_commands","line_end":53,"severity":"medium","line_start":53},{"id":"external_commands:SKILL.md:54:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| `TYPE: site-zip` (`.zip` file) | SITE | `site-zip` + `--path` | `POST /api/sites` (pre-built archi","category":"external_commands","line_end":54,"severity":"medium","line_start":54},{"id":"external_commands:SKILL.md:55:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| `TYPE: file` or `TYPE: markdown` (any other file) | FILE | `file` + `--path` | `POST /api/files` (","category":"external_commands","line_end":55,"severity":"medium","line_start":55},{"id":"external_commands:SKILL.md:56:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| No path (`TYPE: none`), `content_arg` starts with `{` or `[` | JSON | `json` + `--text` | `POST /a","category":"external_commands","line_end":56,"severity":"medium","line_start":56},{"id":"external_commands:SKILL.md:57:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| Anything else | HTML | `html` + `--text` | `POST /api/html` (format=markdown) |","category":"external_commands","line_end":57,"severity":"medium","line_start":57},{"id":"external_commands:SKILL.md:59:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Mode rule: directory/ZIP → SITE. Single file → FILE. `POST /api/sites` accepts a multipart `archive=","category":"external_commands","line_end":59,"severity":"medium","line_start":59},{"id":"external_commands:SKILL.md:63:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"From the probe lines — SITE (dir): `HTML`, `SIZE_KB`, `ENTRY`. SITE (ZIP): `BYTES`, entry override. ","category":"external_commands","line_end":63,"severity":"medium","line_start":63},{"id":"external_commands:SKILL.md:66:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":69,"severity":"medium","line_start":66},{"id":"external_commands:SKILL.md:69:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":71,"severity":"medium","line_start":69},{"id":"external_commands:SKILL.md:71:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"For SITE (entry = server rule: `--entry` > root `index.html` > shallowest `.html`; another page → pa","category":"external_commands","line_end":71,"severity":"medium","line_start":71},{"id":"external_commands:SKILL.md:74:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- No `.html` file in the directory (`ENTRY: none`) → **FAIL** with an explicit error: \"No `.html` fo","category":"external_commands","line_end":74,"severity":"medium","line_start":74},{"id":"external_commands:SKILL.md:75:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- The directory looks like un-built sources (has `package.json` + `src/` but no top-level `.html`) →","category":"external_commands","line_end":75,"severity":"medium","line_start":75},{"id":"external_commands:SKILL.md:76:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":80,"severity":"medium","line_start":76},{"id":"external_commands:SKILL.md:80:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":82,"severity":"medium","line_start":80},{"id":"external_commands:SKILL.md:82:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"`ENTRY:` line is the entry `scripts/publish.mjs` resolved in the built archive.","category":"external_commands","line_end":82,"severity":"medium","line_start":82},{"id":"external_commands:SKILL.md:84:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"**Site limits** (`GET https://brewpage.app/api/limits` returns the current ones): 25 MB per request;","category":"external_commands","line_end":86,"severity":"medium","line_start":84},{"id":"external_commands:SKILL.md:86:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"380 characters / 900 bytes, each file or folder name at most 200 bytes. A big site (`FILES` or `SIZE","category":"external_commands","line_end":86,"severity":"medium","line_start":86},{"id":"external_commands:SKILL.md:89:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":91,"severity":"medium","line_start":89},{"id":"external_commands:SKILL.md:91:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":92,"severity":"medium","line_start":91},{"id":"external_commands:SKILL.md:92:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Pre-checks, folder or ZIP: system files (`.DS_Store`, `__MACOSX/`, `Thumbs.db`) never count and are","category":"external_commands","line_end":92,"severity":"medium","line_start":92},{"id":"external_commands:SKILL.md:93:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"dropped; hidden files and folders are dropped too, except the root `.well-known/`; at least one `.ht","category":"external_commands","line_end":93,"severity":"medium","line_start":93},{"id":"external_commands:SKILL.md:100:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":110,"severity":"medium","line_start":100},{"id":"external_commands:SKILL.md:110:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":113,"severity":"medium","line_start":110},{"id":"external_commands:SKILL.md:113:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- File → topic/purpose of the file (e.g. `api-docs`, `login-page`, `report-q2`)","category":"external_commands","line_end":113,"severity":"medium","line_start":113},{"id":"external_commands:SKILL.md:114:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Text/HTML → main subject or title (e.g. `pricing`, `team-intro`, `changelog`)","category":"external_commands","line_end":114,"severity":"medium","line_start":114},{"id":"external_commands:SKILL.md:115:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- JSON → data type or schema name (e.g. `user-config`, `metrics`)","category":"external_commands","line_end":115,"severity":"medium","line_start":115},{"id":"external_commands:SKILL.md:119:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Resolution: explicit `2` or `4` → suggested slug | `1` → `public` | custom slug → validate it.","category":"external_commands","line_end":119,"severity":"medium","line_start":119},{"id":"external_commands:SKILL.md:126:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":135,"severity":"medium","line_start":126},{"id":"external_commands:SKILL.md:135:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":137,"severity":"medium","line_start":135},{"id":"external_commands:SKILL.md:137:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Resolution: explicit `1` or `3` → `{password_file}` = `''` (no password). `2` + an existing","category":"external_commands","line_end":137,"severity":"medium","line_start":137},{"id":"external_commands:SKILL.md:138:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"local file path → `{password_file}` = that absolute path: the script moves the bytes via private hea","category":"external_commands","line_end":145,"severity":"medium","line_start":138},{"id":"external_commands:SKILL.md:145:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"Delivery mode: NEW sites and non-public HTML pages (format `html`) get a Dedicated subdomain (an exp","category":"external_commands","line_end":145,"severity":"medium","line_start":145},{"id":"external_commands:SKILL.md:146:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"its subdomain and the response reports the mismatch); everything else defaults to Promotion (`path`)","category":"external_commands","line_end":147,"severity":"medium","line_start":146},{"id":"external_commands:SKILL.md:147:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"select `subdomain`, password-protected included. Send it only as `X-Delivery-Mode`, never in authore","category":"external_commands","line_end":147,"severity":"medium","line_start":147},{"id":"external_commands:SKILL.md:148:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"OLD or NEW winner may differ: keep its identity, cohort, mode and exact `link`. Missing/null OLD rou","category":"external_commands","line_end":157,"severity":"medium","line_start":148},{"id":"external_commands:SKILL.md:157:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| `{ns}` | `^[a-z0-9-]{1,32}$` |","category":"external_commands","line_end":157,"severity":"medium","line_start":157},{"id":"external_commands:SKILL.md:158:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| `{days}` | positive integer; `''` = 15 |","category":"external_commands","line_end":158,"severity":"medium","line_start":158},{"id":"external_commands:SKILL.md:159:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| `{entry}` | plain relative file name, no `..`; `''` = auto-detect; sites only |","category":"external_commands","line_end":159,"severity":"medium","line_start":159},{"id":"external_commands:SKILL.md:160:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| `{delivery_mode}` | `''`, `path` or `subdomain` |","category":"external_commands","line_end":160,"severity":"medium","line_start":160},{"id":"external_commands:SKILL.md:161:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| `{password_file}` | `''` or an absolute path to an existing regular file |","category":"external_commands","line_end":161,"severity":"medium","line_start":161},{"id":"external_commands:SKILL.md:164:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"any run is touched, so staged text survives the retry. `{type}` comes from the Step 2 table.","category":"external_commands","line_end":166,"severity":"medium","line_start":164},{"id":"external_commands:SKILL.md:166:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"**SITE, FILE** — `{path}` is the probe's `PATH:` — **EXECUTE** using Bash tool:","category":"external_commands","line_end":166,"severity":"medium","line_start":166},{"id":"external_commands:SKILL.md:167:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":169,"severity":"medium","line_start":167},{"id":"external_commands:SKILL.md:169:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":171,"severity":"medium","line_start":169},{"id":"external_commands:SKILL.md:171:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"**HTML text, JSON** — `{content}` is the exact text or JSON, verbatim — **EXECUTE** using Bash tool:","category":"external_commands","line_end":172,"severity":"medium","line_start":171},{"id":"external_commands:SKILL.md:172:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":174,"severity":"medium","line_start":172},{"id":"external_commands:SKILL.md:174:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":177,"severity":"medium","line_start":174},{"id":"external_commands:SKILL.md:177:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"of at most 6,000 characters. The first `stage` call passes `--run ''` and prints `RUN: brewpage.XXXX","category":"external_commands","line_end":177,"severity":"medium","line_start":177},{"id":"external_commands:SKILL.md:180:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":182,"severity":"medium","line_start":180},{"id":"external_commands:SKILL.md:182:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":184,"severity":"medium","line_start":182},{"id":"external_commands:SKILL.md:184:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":186,"severity":"medium","line_start":184},{"id":"external_commands:SKILL.md:186:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":188,"severity":"medium","line_start":186},{"id":"external_commands:SKILL.md:188:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":190,"severity":"medium","line_start":188},{"id":"external_commands:SKILL.md:190:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":194,"severity":"medium","line_start":190},{"id":"external_commands:SKILL.md:194:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"**Success** (the script printed `OK {url}`):","category":"external_commands","line_end":195,"severity":"medium","line_start":194},{"id":"external_commands:SKILL.md:195:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":199,"severity":"medium","line_start":195},{"id":"external_commands:SKILL.md:199:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":201,"severity":"medium","line_start":199},{"id":"external_commands:SKILL.md:201:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"**Success for SITE** (the script printed `OK {url} | Files: {count}`):","category":"external_commands","line_end":202,"severity":"medium","line_start":201},{"id":"external_commands:SKILL.md:202:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":209,"severity":"medium","line_start":202},{"id":"external_commands:SKILL.md:209:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":214,"severity":"medium","line_start":209},{"id":"external_commands:SKILL.md:214:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"For a private (non-`public`) namespace, append one short line after the link (skip if the reply must","category":"external_commands","line_end":214,"severity":"medium","line_start":214},{"id":"external_commands:SKILL.md:218:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"**Needs confirmation** (the script printed `CONFIRM: ...`, exit 2; nothing uploaded): show the manif","category":"external_commands","line_end":219,"severity":"medium","line_start":218},{"id":"external_commands:SKILL.md:219:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"lines, name the flagged entries, ask ONE `AskUserQuestion` (publish anyway / cancel). Publish anyway","category":"external_commands","line_end":220,"severity":"medium","line_start":219},{"id":"external_commands:SKILL.md:220:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"re-run the identical command with ` --confirmed --review-sha256 '{review_sha256}'` appended, using t","category":"external_commands","line_end":221,"severity":"medium","line_start":220},{"id":"external_commands:SKILL.md:221:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"`REVIEW_SHA256` printed beside the approved manifest. Changed archive bytes produce a fresh manifest","category":"external_commands","line_end":226,"severity":"medium","line_start":221},{"id":"external_commands:SKILL.md:226:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"**Error** (the script printed `FAILED: ...`):","category":"external_commands","line_end":227,"severity":"medium","line_start":226},{"id":"external_commands:SKILL.md:227:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":229,"severity":"medium","line_start":227},{"id":"external_commands:SKILL.md:229:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":235,"severity":"medium","line_start":229},{"id":"external_commands:SKILL.md:235:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| 422 | No servable file left (every file has an unsupported or missing extension) | Add the `.html`","category":"external_commands","line_end":245,"severity":"medium","line_start":235},{"id":"external_commands:SKILL.md:245:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Scripts: `scripts/brewpage-run.sh` (the only entry), `scripts/brewpage-lib.sh` (sourced helpers), ","category":"external_commands","line_end":245,"severity":"medium","line_start":245},{"id":"external_commands:SKILL.md:246:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- **`format` is a query param**: `/api/html` reads only `?format=` from the URL; a body `format` key","category":"external_commands","line_end":246,"severity":"medium","line_start":246},{"id":"external_commands:SKILL.md:247:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- History: `<project-root>/.claude/brewpage-history.md` (root: `CLAUDE_PROJECT_DIR` → git toplevel →","category":"external_commands","line_end":247,"severity":"medium","line_start":247},{"id":"external_commands:SKILL.md:248:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Run dirs: private `brewpage.XXXXXX` under `$TMPDIR` (else `/tmp`), outside the protected `.claude/","category":"external_commands","line_end":248,"severity":"medium","line_start":248},{"id":"external_commands:SKILL.md:249:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Permissions: model-invocable, so `allowed-tools` holds one scoped Bash rule that matches every ste","category":"external_commands","line_end":250,"severity":"medium","line_start":249},{"id":"external_commands:SKILL.md:250:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- TTL default is `15` days; stored `createdAt`/`expiresAt` are authoritative: a deduplicated winner ","category":"external_commands","line_end":250,"severity":"medium","line_start":250},{"id":"external_commands:SKILL.md:251:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Use returned `ownerLink` and stored identity metadata for owner API operations; never parse NEW ro","category":"external_commands","line_end":252,"severity":"medium","line_start":251},{"id":"external_commands:SKILL.md:252:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- To **update a published site**, `PUT` the replacement bundle to its apex owner API with `X-Owner-T","category":"external_commands","line_end":252,"severity":"medium","line_start":252},{"id":"external_commands:SKILL.md:253:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Owner-only `GET`/`PATCH /api/{type}/{ns}/{id}/hosting` supports `html|files|json|kv|sites` with `X","category":"external_commands","line_end":253,"severity":"medium","line_start":253},{"id":"external_commands:SKILL.md:254:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- NEW password-protected links stay clean: never generate or append `?p=`. API requests use `X-Passw","category":"external_commands","line_end":254,"severity":"medium","line_start":254},{"id":"external_commands:SKILL.md:255:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- **SITE bundles are allowlisted**: the directory build drops dot-entries (`.env`, `.git/`, `.DS_Sto","category":"external_commands","line_end":255,"severity":"medium","line_start":255},{"id":"external_commands:SKILL.md:256:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"- Preserve `.link` exactly: NEW subdomain roots retain `/`; OLD links retain their existing form. Ne","category":"external_commands","line_end":256,"severity":"medium","line_start":256},{"id":"external_commands:SKILL.md:23:shell-command-substitution","file":"SKILL.md","pattern":"Shell command substitution","snippet":"- Never quote the script path, prefix or chain the command (`cd`, `VAR=...`, `;`, `&&`, `|`, `$(...)","category":"external_commands","line_end":23,"severity":"medium","line_start":23},{"id":"external_commands:SKILL.md:23:template-literal-with-command-substitution","file":"SKILL.md","pattern":"Template literal with command substitution","snippet":"- Never quote the script path, prefix or chain the command (`cd`, `VAR=...`, `;`, `&&`, `|`, `$(...)","category":"external_commands","line_end":23,"severity":"medium","line_start":23},{"id":"network:SKILL.md:84:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"**Site limits** (`GET https://brewpage.app/api/limits` returns the current ones): 25 MB per request;","category":"network","line_end":84,"severity":"low","line_start":84},{"id":"network:SKILL.md:198:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"Optional: sign in free (Google or any email) at https://account.brewpage.app to manage publications ","category":"network","line_end":198,"severity":"low","line_start":198},{"id":"network:SKILL.md:206:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"Optional: sign in free (Google or any email) at https://account.brewpage.app to manage publications ","category":"network","line_end":206,"severity":"low","line_start":206},{"id":"network:SKILL.md:265:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"| **[brewpage.app](https://brewpage.app)** | Free instant hosting — HTML, JSON, files, sites, KV. No","category":"network","line_end":265,"severity":"low","line_start":265},{"id":"network:SKILL.md:266:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"| **[brewcode](https://github.com/kochetkov-ma/claude-brewcode)** | Claude Code plugin suite — skill","category":"network","line_end":266,"severity":"low","line_start":266},{"id":"filesystem:SKILL.md:197:hidden-file-access","file":"SKILL.md","pattern":"Hidden file access","snippet":"Owner token saved to <project-root>/.claude/brewpage-history.md (mode 600, git-ignored)","category":"filesystem","line_end":197,"severity":"medium","line_start":197},{"id":"filesystem:SKILL.md:205:hidden-file-access","file":"SKILL.md","pattern":"Hidden file access","snippet":"Owner token saved to <project-root>/.claude/brewpage-history.md (mode 600, git-ignored)","category":"filesystem","line_end":205,"severity":"medium","line_start":205},{"id":"filesystem:SKILL.md:247:hidden-file-access","file":"SKILL.md","pattern":"Hidden file access","snippet":"- History: `<project-root>/.claude/brewpage-history.md` (root: `CLAUDE_PROJECT_DIR` → git toplevel →","category":"filesystem","line_end":247,"severity":"medium","line_start":247},{"id":"sensitive:SKILL.md:255:environment-file-access","file":"SKILL.md","pattern":"Environment file access","snippet":"- **SITE bundles are allowlisted**: the directory build drops dot-entries (`.env`, `.git/`, `.DS_Sto","category":"sensitive","line_end":255,"severity":"high","line_start":255},{"id":"sensitive:SKILL.md:92:sqlite-database-file","file":"SKILL.md","pattern":"SQLite database file","snippet":"Pre-checks, folder or ZIP: system files (`.DS_Store`, `__MACOSX/`, `Thumbs.db`) never count and are","category":"sensitive","line_end":92,"severity":"medium","line_start":92},{"id":"sensitive:SKILL.md:255:sqlite-database-file","file":"SKILL.md","pattern":"SQLite database file","snippet":"- **SITE bundles are allowlisted**: the directory build drops dot-entries (`.env`, `.git/`, `.DS_Sto","category":"sensitive","line_end":255,"severity":"medium","line_start":255},{"id":"blocker:SKILL.md:238:system-reconnaissance","file":"SKILL.md","pattern":"System reconnaissance","snippet":"Unstorable metadata (tags, names, control characters) is stored as the closest valid value with a no","category":"blocker","line_end":238,"severity":"low","line_start":238},{"id":"obfuscation:scripts/brewpage-lib.sh:102:heuristic-very-high-entropy-string-6-03-bits-lik","file":"scripts/brewpage-lib.sh","pattern":"[HEURISTIC] Very high entropy string (6.03 bits) - likely encoded/encrypted payload","snippet":" in *[!ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.]*) echo ","category":"obfuscation","line_end":102,"severity":"high","line_start":102}],"finding_verdicts":[{"id":"network:LICENSE:3:hardcoded-url","reason":"The GitHub URL is copyright attribution in the MIT license, not an executed network request.","verdict":"false_positive","confidence":1},{"id":"network:README.md:117:hardcoded-url","reason":"This documented DELETE example targets the declared hosting service with a placeholder owner token, not an unrelated collection endpoint.","verdict":"false_positive","confidence":0.99},{"id":"network:README.md:127:hardcoded-url","reason":"The GitHub URL documents optional plugin installation from the author's repository; reading the documentation does not execute it.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:README.md:68:environment-file-access","reason":"The documentation describes excluding .env and other hidden entries from directory bundles, not reading their secrets.","verdict":"false_positive","confidence":1},{"id":"sensitive:README.md:68:sqlite-database-file","reason":"Thumbs.db is named as an excluded system file, not a database opened or uploaded by this statement.","verdict":"false_positive","confidence":1},{"id":"blocker:README.md:91:system-reconnaissance","reason":"This paragraph describes preserving server-returned publication identity and routing metadata, not collecting system information.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-lib.sh:51:ruby-shell-backtick-execution","reason":"The backticks are literal Markdown inside a quoted HEADER heredoc; the displayed curl example is not executed.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-lib.sh:52:ruby-shell-backtick-execution","reason":"The quoted HEADER heredoc stores owner-operation documentation literally, so its backticks cannot invoke shell commands.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-lib.sh:62:ruby-shell-backtick-execution","reason":"Backticks occur inside a single-quoted printf format and produce literal Markdown code spans.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-lib.sh:65:ruby-shell-backtick-execution","reason":"The single-quoted printf format emits literal Markdown backticks; argument values are formatted as data.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-lib.sh:166:ruby-shell-backtick-execution","reason":"The backticks surround set -- in a shell comment and are never executed.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-lib.sh:187:ruby-shell-backtick-execution","reason":"The backticks surround a type name in a shell comment, not executable source.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-lib.sh:8:shell-command-substitution","reason":"The substitution canonicalizes a quoted directory using cd and pwd; the path is not evaluated as shell code.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:9:shell-command-substitution","reason":"The quoted HOME lookup identifies a profile directory using cd and pwd, without executing variable contents.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:10:shell-command-substitution","reason":"The substitution canonicalizes the quoted configuration directory to avoid treating a user profile as a project root.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:21:shell-command-substitution","reason":"The fixed git rev-parse command discovers the workspace root; it does not run user-provided command text.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:scripts/brewpage-lib.sh:32:shell-command-substitution","reason":"dirname receives a quoted directory argument during upward root discovery; no eval or dynamic shell source is used.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:37:shell-command-substitution","reason":"This invokes the local root-discovery function and captures its path for protected history placement.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:47:shell-command-substitution","reason":"dirname resolves the bundled history helper; Python receives quoted arguments and enforces ownership, symlink, and Git safeguards.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:63:shell-command-substitution","reason":"Fixed date formatting and helper-path discovery support history writing; token values are printf arguments, not shell source.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:66:shell-command-substitution","reason":"The substitutions provide a timestamp and bundled helper path; the history row is piped as data to the protected writer.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:105:shell-command-substitution","reason":"The fixed find invocation checks that the validated run directory has mode 0700 before use or cleanup.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:190:shell-command-substitution","reason":"A fixed jq filter extracts the reader link from response data; the result is not evaluated as shell code.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:192:shell-command-substitution","reason":"The local error formatter extracts a bounded, control-normalized message without evaluating response text.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:199:shell-command-substitution","reason":"A fixed jq filter extracts the service-issued owner token for protected local history, not chat output or external forwarding.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:201:shell-command-substitution","reason":"The fixed jq expression extracts the returned owner API link as data for history; no command is constructed from it.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:202:shell-command-substitution","reason":"A fixed jq projection serializes publication metadata for local history without evaluating server-provided values.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:204:shell-command-substitution","reason":"The fixed jq filter reads a site file count for reporting and does not execute the response.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:246:shell-command-substitution","reason":"The quoted Python heredoc hashes a regular, owned archive opened with O_NOFOLLOW to bind confirmation to its bytes.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-lib.sh:265:shell-command-substitution","reason":"The fixed sed expression extracts the manifest entry as data; upload URL construction later URI-encodes it.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:scripts/brewpage-lib.sh:62:template-literal-with-command-substitution","reason":"This is a single-quoted shell printf format containing literal Markdown, not a JavaScript template or executable backtick expression.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-lib.sh:65:template-literal-with-command-substitution","reason":"Markdown backticks remain literal in the single-quoted format; nearby substitutions run fixed date and path helpers.","verdict":"false_positive","confidence":1},{"id":"network:scripts/brewpage-lib.sh:51:hardcoded-url","reason":"The service URL is written as literal owner-operation documentation by a quoted heredoc, not contacted by that line.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:84:path-traversal-sequence","reason":"The traversal sequences occur in rejection patterns that return failure; subsequent site entry validation also rejects parent-directory segments.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/brewpage-lib.sh:10:hidden-file-access","reason":"The .claude path comparison identifies profile directories without reading configuration contents.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/brewpage-lib.sh:14:hidden-file-access","reason":"This comment documents project-root discovery through .git and .claude markers, not secret-file access.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:28:hidden-file-access","reason":"Directory-existence checks locate the workspace root and skip profile parents; no hidden file contents are extracted.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/brewpage-lib.sh:38:hidden-file-access","reason":"The path defines documented owner-token history protected by mode 0600, Git exclusion, and no-follow file operations.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/brewpage-lib.sh:8:standard-device-file-access","reason":"The /dev/null redirection suppresses directory-resolution errors and accesses no sensitive device data.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:9:standard-device-file-access","reason":"The /dev/null redirection discards harmless errors from HOME canonicalization.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:10:standard-device-file-access","reason":"The /dev/null redirection only suppresses configuration-directory lookup errors.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:21:standard-device-file-access","reason":"Git discovery errors are discarded through /dev/null; this is not device inspection.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:46:standard-device-file-access","reason":"command -v checks the Python dependency and discards the executable path through /dev/null.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:132:standard-device-file-access","reason":"The /dev/null output sink suppresses dependency-check output for jq.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:133:standard-device-file-access","reason":"Password validation discards jq output and diagnostics to avoid exposing the selected password in terminal output.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/brewpage-lib.sh:148:standard-device-file-access","reason":"The jq availability check discards its path with /dev/null; no sensitive device is read.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:160:standard-device-file-access","reason":"The redirection suppresses password-filter diagnostics while the validated header is written privately under umask 077.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/brewpage-lib.sh:183:standard-device-file-access","reason":"The /dev/null redirection suppresses JSON parsing errors from the bounded error formatter.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:196:standard-device-file-access","reason":"jq uses its exit status to check merge ownership and discards output through /dev/null.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:212:standard-device-file-access","reason":"The /dev/null redirection suppresses optional notice-parser errors; it does not access device information.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-lib.sh:93:temp-file-creation","reason":"mktemp allocates a unique run directory under umask 077, with ownership, mode, and symlink checks before reuse.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/brewpage-lib.sh:226:python-file-write-append","reason":"The archive snapshot destination uses exclusive creation, O_NOFOLLOW, and mode 0600 inside the private run directory.","verdict":"false_positive","confidence":0.99},{"id":"blocker:scripts/brewpage-lib.sh:100:system-reconnaissance","reason":"The branch rejects an invalid publish run identifier; it performs no system reconnaissance.","verdict":"false_positive","confidence":1},{"id":"blocker:scripts/brewpage-lib.sh:102:system-reconnaissance","reason":"This case pattern restricts run identifiers to ASCII letters, digits, and periods, preventing unsafe path characters.","verdict":"false_positive","confidence":1},{"id":"blocker:scripts/brewpage-lib.sh:125:system-reconnaissance","reason":"The jq expression constructs a validated password header or returns an error; it does not enumerate system information.","verdict":"false_positive","confidence":1},{"id":"blocker:scripts/brewpage-lib.sh:127:system-reconnaissance","reason":"This comment describes the explicitly supplied password-file contract, not host inspection.","verdict":"false_positive","confidence":1},{"id":"blocker:scripts/brewpage-lib.sh:251:system-reconnaissance","reason":"getuid and file metadata verify that an archive snapshot is regular, owned, and single-linked before hashing.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:6:shell-command-substitution","reason":"Fixed cd, dirname, and pwd commands resolve the bundled helper directory from a quoted script path.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:25:shell-command-substitution","reason":"The local preparation function allocates a private temporary directory and captures its generated name.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:47:shell-command-substitution","reason":"The quoted directory argument is canonicalized by cd and pwd for a read-only probe, not evaluated as command text.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:49:shell-command-substitution","reason":"A fixed find and filtering pipeline lists HTML entry candidates; filename contents are not executed.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:52:shell-command-substitution","reason":"The fixed awk, sort, sed, and cut pipeline selects the shallowest HTML candidate from data.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:55:shell-command-substitution","reason":"Fixed find and counting commands provide source-tree estimates without executing file contents.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:56:shell-command-substitution","reason":"du and cut calculate a size estimate for the quoted selected directory.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:58:shell-command-substitution","reason":"Quoted dirname, basename, cd, and pwd operations construct the selected file's absolute path without eval.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:61:shell-command-substitution","reason":"wc reads the selected file through quoted redirection and returns its byte count for the probe.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:62:shell-command-substitution","reason":"file receives the quoted path after -- to report its MIME type, not execute it.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:73:shell-command-substitution","reason":"The fixed HTTPS GET fetches public upload limits from the declared hosting service without transmitting local content.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:103:shell-command-substitution","reason":"wc counts staged text bytes in the validated private run directory; the count is printed as data.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:127:shell-command-substitution","reason":"The expression is arithmetic expansion incrementing a source counter, not shell command substitution.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-run.sh:128:shell-command-substitution","reason":"The $((sources + 1)) expression increments an internal argument counter and executes no command.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-run.sh:129:shell-command-substitution","reason":"This is integer arithmetic for validating mutually exclusive source options, not dynamic command execution.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/brewpage-run.sh:188:shell-command-substitution","reason":"The documented page upload sends selected content to the fixed service endpoint with validated namespace and retention values.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:scripts/brewpage-run.sh:194:shell-command-substitution","reason":"The documented JSON upload uses a fixed service endpoint after JSON validation and explicit access choices in the workflow.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:scripts/brewpage-run.sh:199:shell-command-substitution","reason":"The declared file-upload operation uses curl form-path escaping and a fixed service URL; it does not evaluate file paths as shell source.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:scripts/brewpage-run.sh:205:shell-command-substitution","reason":"The bundled offline packer receives quoted paths as arguments, not shell code; its source-file race is recorded separately.","verdict":"false_positive","confidence":0.97},{"id":"external_commands:scripts/brewpage-run.sh:212:shell-command-substitution","reason":"The bundled offline inspector receives the private ZIP snapshot as a quoted argument and returns a review manifest.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/brewpage-run.sh:216:shell-command-substitution","reason":"The site upload uses the fixed HTTPS API and URI-encodes the resolved entry through a fixed jq expression.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:scripts/brewpage-run.sh:217:shell-command-substitution","reason":"The local form_file helper escapes the archive path for curl's multipart grammar; the result remains a quoted data argument.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/brewpage-run.sh:73:hardcoded-url","reason":"The URL fetches public service limits and contains no local data or secret parameters.","verdict":"false_positive","confidence":0.99},{"id":"network:scripts/brewpage-run.sh:188:hardcoded-url","reason":"This is the advertised brewpage.app Markdown publishing endpoint, used for selected content rather than concealed data collection.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/brewpage-run.sh:194:hardcoded-url","reason":"The fixed brewpage.app JSON endpoint implements the stated publishing purpose; namespace and lifetime values are validated.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/brewpage-run.sh:199:hardcoded-url","reason":"The fixed brewpage.app file endpoint intentionally receives the user-selected file as documented.","verdict":"false_positive","confidence":0.98},{"id":"network:scripts/brewpage-run.sh:216:hardcoded-url","reason":"The fixed site endpoint is the declared destination for reviewed archives, not an unrelated exfiltration destination.","verdict":"false_positive","confidence":0.98},{"id":"filesystem:scripts/brewpage-run.sh:62:standard-device-file-access","reason":"The /dev/null sink suppresses MIME-probe errors and exposes no device data.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-run.sh:71:standard-device-file-access","reason":"The jq availability check discards its path through /dev/null.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-run.sh:77:standard-device-file-access","reason":"The /dev/null redirection only suppresses limit-response parsing diagnostics.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-run.sh:136:standard-device-file-access","reason":"The Python dependency check discards command lookup output through /dev/null.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/brewpage-run.sh:193:standard-device-file-access","reason":"The /dev/null sink suppresses jq diagnostics while invalid JSON fails before upload.","verdict":"false_positive","confidence":1},{"id":"blocker:scripts/brewpage-run.sh:160:system-reconnaissance","reason":"id is a local run-identifier variable declaration, not a system identity command.","verdict":"false_positive","confidence":1},{"id":"blocker:scripts/brewpage-run.sh:178:system-reconnaissance","reason":"The line checks whether staged content exists and reports its run identifier on failure; it performs no host reconnaissance.","verdict":"false_positive","confidence":1},{"id":"blocker:scripts/brewpage-run.sh:193:system-reconnaissance","reason":"jq empty validates the selected JSON payload, not the operating system.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/history.py:55:python-subprocess-run","reason":"Git runs with an argument list and fixed repository-safety subcommands; shell execution is not enabled.","verdict":"false_positive","confidence":0.98},{"id":"filesystem:scripts/history.py:33:python-file-write-append","reason":"The writer uses an exclusive no-follow temporary file, fsync, and atomic replacement for constrained history and ignore destinations.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/history.py:41:python-os-file-operations","reason":"unlink removes only the randomized temporary filename relative to the already opened directory descriptor.","verdict":"false_positive","confidence":0.99},{"id":"env_access:scripts/history.py:55:python-environment-access","reason":"The child inherits the normal process environment with LC_ALL fixed for Git diagnostics; no environment values are uploaded.","verdict":"false_positive","confidence":0.98},{"id":"env_access:scripts/history.py:65:python-environment-access","reason":"GIT_DIR presence triggers failure when Git is unavailable, preventing token history from bypassing repository safeguards.","verdict":"false_positive","confidence":0.99},{"id":"blocker:scripts/history.py:23:system-reconnaissance","reason":"getuid and fstat enforce regular, owned, single-linked history and ignore files, not user profiling.","verdict":"false_positive","confidence":0.99},{"id":"blocker:scripts/history.py:82:system-reconnaissance","reason":"This raises an error for an invalid history operation or destination; it gathers no system information.","verdict":"false_positive","confidence":1},{"id":"blocker:scripts/history.py:87:system-reconnaissance","reason":"Ownership and permission checks reject workspaces writable by others before token-history changes.","verdict":"false_positive","confidence":0.99},{"id":"blocker:scripts/history.py:99:system-reconnaissance","reason":"The check verifies ownership and write permissions on history directories to protect stored owner tokens.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/publish.mjs:4:node-js-child-process-module","reason":"The import supports fixed zip and unzip subprocesses using argument arrays without a shell, not arbitrary command evaluation.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:scripts/publish.mjs:153:synchronous-spawn","reason":"unzip runs fixed integrity or listing flags without shell execution, bounded by output limits and a ten-second timeout.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:scripts/publish.mjs:205:synchronous-spawn","reason":"zip receives fixed arguments and selected filenames through stdin without a shell; the separate source-validation race is recorded semantically.","verdict":"false_positive","confidence":0.97},{"id":"external_commands:scripts/publish.mjs:2:ruby-shell-backtick-execution","reason":"The backticks are documentation in a JavaScript block comment and execute no shell commands.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:26:ruby-shell-backtick-execution","reason":"The backticks mark .well-known in a JavaScript comment, not executable command substitution.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:29:ruby-shell-backtick-execution","reason":"The comment uses Markdown code spans to document allowed extensionless files.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:30:ruby-shell-backtick-execution","reason":"This JavaScript template literal constructs a path prefix from a constant and does not invoke a shell.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:37:ruby-shell-backtick-execution","reason":"Backticks annotate unzip options in a JavaScript comment, not executable source.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:71:ruby-shell-backtick-execution","reason":"The JavaScript template literal formats a failure message for console output, without code evaluation.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:81:ruby-shell-backtick-execution","reason":"The template literal formats an argument-validation error and is never passed to a shell.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:90:ruby-shell-backtick-execution","reason":"The template literal produces an invalid-entry error; interpolated values remain string data.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:120:ruby-shell-backtick-execution","reason":"The template literal reports a missing archive entry and performs no command execution.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:132:ruby-shell-backtick-execution","reason":"The backticks appear only around a parameter name in a comment describing executable-file checks.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:141:ruby-shell-backtick-execution","reason":"The template literal formats a missing-tool error after filesystem lookup; no command string is evaluated.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:146:ruby-shell-backtick-execution","reason":"This JavaScript template literal formats a subprocess exit status as text.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:147:ruby-shell-backtick-execution","reason":"The template literal formats an existing subprocess error rather than executing its contents.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:148:ruby-shell-backtick-execution","reason":"The template literal returns a signal-related error string without evaluating it.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:151:ruby-shell-backtick-execution","reason":"Backticks identify unzip in a JavaScript documentation comment and have no runtime effect.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:156:ruby-shell-backtick-execution","reason":"The template literal composes a verification-error message stored in a returned object.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:159:ruby-shell-backtick-execution","reason":"The backticks annotate unzip checking and listing commands in a comment; actual calls use argument arrays.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:173:ruby-shell-backtick-execution","reason":"The JavaScript template literal prints a manifest heading and does not execute the archive basename.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:174:ruby-shell-backtick-execution","reason":"The template literal prints each archive filename as text, not shell source.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:175:ruby-shell-backtick-execution","reason":"The template literal prints the manifest file count and executes no external command.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:176:ruby-shell-backtick-execution","reason":"The JavaScript template literal prints the archive byte count as text.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:177:ruby-shell-backtick-execution","reason":"The template literal emits the resolved entry field in the manifest, not a command.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:178:ruby-shell-backtick-execution","reason":"Nested JavaScript template literals format skipped-file counts and names without invoking a shell.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:179:ruby-shell-backtick-execution","reason":"The template literal reports unexpected archive entries for review; filenames are not evaluated.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:181:ruby-shell-backtick-execution","reason":"The template literal reports sensitive-looking filenames to trigger confirmation rather than executing them.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:183:ruby-shell-backtick-execution","reason":"This template literal emits a fixed confirmation-status label based on a Boolean.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:192:ruby-shell-backtick-execution","reason":"The template literal formats a missing-directory error and does not execute the path.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:198:ruby-shell-backtick-execution","reason":"The template literal reports that no files survived the allowlist filter.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:201:ruby-shell-backtick-execution","reason":"The backticks surround mktemp in a source comment explaining ZIP output handling.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:206:ruby-shell-backtick-execution","reason":"The JavaScript template literal constructs newline-delimited filenames for zip stdin; no shell parses this string.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:212:ruby-shell-backtick-execution","reason":"The template literal formats a zip failure after deleting the partial archive.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:224:ruby-shell-backtick-execution","reason":"The JavaScript template literal formats an archive name-set mismatch error rather than executing code.","verdict":"false_positive","confidence":1},{"id":"external_commands:scripts/publish.mjs:232:ruby-shell-backtick-execution","reason":"The template literal formats a file-existence error and is not interpreted by a shell.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/publish.mjs:5:synchronous-file-operations","reason":"The filesystem imports implement selected-tree collection, bounded archive inspection, tool lookup, and temporary-output cleanup; none independently implies unauthorized access.","verdict":"false_positive","confidence":0.98},{"id":"filesystem:scripts/publish.mjs:44:synchronous-file-operations","reason":"readFileSync reads the selected ZIP after a 25 MiB size check to enforce central-directory inspection budgets.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:scripts/publish.mjs:110:hidden-file-access","reason":"This line rejects carriage returns and newlines in relative filenames; it does not open hidden files.","verdict":"false_positive","confidence":1},{"id":"filesystem:scripts/publish.mjs:201:temp-file-creation","reason":"The occurrence is a comment about avoiding precreated empty ZIP files; the caller supplies an output path inside its private run directory.","verdict":"false_positive","confidence":1},{"id":"env_access:scripts/publish.mjs:139:environment-variable-access-dot-notation","reason":"PATHEXT is used only for local executable lookup on Windows and is not sent to the hosting service.","verdict":"false_positive","confidence":0.99},{"id":"env_access:scripts/publish.mjs:140:environment-variable-access-dot-notation","reason":"PATH is read for local zip and unzip availability checks; the variable value is not uploaded.","verdict":"false_positive","confidence":0.99},{"id":"env_access:scripts/publish.mjs:139:environment-variable-object","reason":"Only PATHEXT is selected for executable suffix lookup; the process environment is not enumerated or transmitted.","verdict":"false_positive","confidence":0.99},{"id":"env_access:scripts/publish.mjs:140:environment-variable-object","reason":"Only PATH is selected to find required tools locally, not to collect environment secrets.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:scripts/publish.mjs:33:ssh-private-key-file","reason":"id_rsa and id_ed25519 are rejection-review patterns for sensitive filenames; this regular expression does not read private keys.","verdict":"false_positive","confidence":1},{"id":"sensitive:scripts/publish.mjs:33:environment-file-access","reason":"The .env string belongs to a sensitive-name detector used to require review, not an environment-file read.","verdict":"false_positive","confidence":1},{"id":"sensitive:scripts/publish.mjs:139:environment-file-access","reason":"process.env.PATHEXT reads executable suffix configuration, not a .env file or credential.","verdict":"false_positive","confidence":1},{"id":"sensitive:scripts/publish.mjs:140:environment-file-access","reason":"process.env.PATH reads executable search directories and does not open environment files.","verdict":"false_positive","confidence":1},{"id":"sensitive:scripts/publish.mjs:24:sqlite-database-file","reason":"Thumbs.db occurs in a system-file exclusion expression, not a SQLite connection or database read.","verdict":"false_positive","confidence":1},{"id":"blocker:scripts/publish.mjs:37:system-reconnaissance","reason":"The comment documents unzip output-buffer limits and does not inspect the host.","verdict":"false_positive","confidence":1},{"id":"blocker:scripts/publish.mjs:48:system-reconnaissance","reason":"The line rejects archives lacking a valid central directory; it processes archive structure, not system identity.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:18:ruby-shell-backtick-execution","reason":"The backticks mark the allowed-tools field in Markdown; they do not execute commands or claim security audit approval.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:21:ruby-shell-backtick-execution","reason":"Markdown code spans document single-quote escaping so argument values remain data rather than executable shell source.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:22:ruby-shell-backtick-execution","reason":"The empty single-quoted argument is documented inside a Markdown code span, not executed through backticks.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:23:ruby-shell-backtick-execution","reason":"Inline code spans list prohibited command chaining and substitution syntax, not instructions to execute those expressions.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:26:ruby-shell-backtick-execution","reason":"The backticks identify the stage subcommand in prose about splitting long inputs.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:27:ruby-shell-backtick-execution","reason":"These code spans describe status lines printed by the publisher; they are not shell command substitutions.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:28:ruby-shell-backtick-execution","reason":"The code spans describe confirmation and failure output, with no executable backtick expression.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:35:ruby-shell-backtick-execution","reason":"The ARGUMENTS variable is a documented skill input, not a backtick command evaluated by a shell.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:36:ruby-shell-backtick-execution","reason":"Markdown code spans document the retention option and its default value.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:37:ruby-shell-backtick-execution","reason":"The entry option appears as inline Markdown describing a validated site filename.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:38:ruby-shell-backtick-execution","reason":"The delivery-mode option is documented as an enumerated argument, with other values rejected before upload.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:39:ruby-shell-backtick-execution","reason":"The content_arg name is an inline code label for user-selected text or a path, not executable shell text.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:44:ruby-shell-backtick-execution","reason":"Inline code labels describe the read-only probe input and output fields.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:47:ruby-shell-backtick-execution","reason":"This opens a Markdown bash fence around the documented read-only probe command; it is not backtick execution.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:49:ruby-shell-backtick-execution","reason":"This is the closing Markdown fence for the read-only probe example.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:51:ruby-shell-backtick-execution","reason":"The table header marks the publish subcommand with inline Markdown backticks.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:53:ruby-shell-backtick-execution","reason":"The table documents directory classification and the declared site endpoint; code spans do not execute their contents.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:54:ruby-shell-backtick-execution","reason":"The table documents ZIP classification and site upload arguments, not shell substitution.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:55:ruby-shell-backtick-execution","reason":"The row maps a selected local file to the documented file-upload endpoint using inline code formatting.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:56:ruby-shell-backtick-execution","reason":"Inline code describes JSON detection and the fixed JSON endpoint; no expression is evaluated by backticks.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:57:ruby-shell-backtick-execution","reason":"This table row documents the text publishing type and its fixed endpoint.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:59:ruby-shell-backtick-execution","reason":"The code spans describe multipart field names and the declared site API, not commands executed through backticks.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:63:ruby-shell-backtick-execution","reason":"Backticks mark probe statistics such as byte counts and entry names for reporting.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:66:ruby-shell-backtick-execution","reason":"This opens a plain Markdown fence containing a pre-publication statistics template.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:69:ruby-shell-backtick-execution","reason":"This closes the statistics template fence and contains no executable command.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:71:ruby-shell-backtick-execution","reason":"Inline code documents site entry precedence and failure when no HTML entry exists.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:74:ruby-shell-backtick-execution","reason":"The code spans identify HTML files and the missing-entry status in an instruction to stop publication.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:75:ruby-shell-backtick-execution","reason":"The filenames identify source-tree markers and suggested build directories; the instruction warns against publishing project sources.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:76:ruby-shell-backtick-execution","reason":"This opens a Markdown fence for the site statistics display, not executable backtick source.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:80:ruby-shell-backtick-execution","reason":"This closes a Markdown site-statistics template with no command substitution.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:82:ruby-shell-backtick-execution","reason":"The backticks label a manifest field and the bundled packer path in explanatory prose.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:84:ruby-shell-backtick-execution","reason":"The code span documents a fixed HTTPS GET for public service limits rather than dynamic shell execution.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:86:ruby-shell-backtick-execution","reason":"The code spans name file-count and size statistics used to decide when to check service limits.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:89:ruby-shell-backtick-execution","reason":"This is a Markdown bash fence around the fixed limits subcommand, not a shell backtick operator.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:91:ruby-shell-backtick-execution","reason":"This closes the documented limits command fence.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:92:ruby-shell-backtick-execution","reason":"Inline code lists system filenames that are excluded, not commands to read or execute them.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:93:ruby-shell-backtick-execution","reason":"Backticks mark the allowed root metadata directory and required HTML extension in prose.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:100:ruby-shell-backtick-execution","reason":"This opens a Markdown fence containing namespace-choice text for the user.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:110:ruby-shell-backtick-execution","reason":"This closes the namespace-choice text fence, not a shell expression.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:113:ruby-shell-backtick-execution","reason":"The inline code spans are example namespace slugs based on the selected file topic.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:114:ruby-shell-backtick-execution","reason":"Backticks format example namespace slugs for text pages.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:115:ruby-shell-backtick-execution","reason":"The inline code spans contain example namespace slugs for JSON documents, not executable code.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:119:ruby-shell-backtick-execution","reason":"The numeric choices and public namespace are literal values in a documented explicit-consent workflow.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:126:ruby-shell-backtick-execution","reason":"This opens a Markdown fence for the password-choice question, not shell source.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:135:ruby-shell-backtick-execution","reason":"This closes the password-choice question fence.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:137:ruby-shell-backtick-execution","reason":"Inline code documents explicit password choices and the empty argument for no password.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:138:ruby-shell-backtick-execution","reason":"The placeholder identifies a user-supplied password-file path handled through private transport files, not shell source.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:145:ruby-shell-backtick-execution","reason":"Backticks identify HTML format and delivery-mode values in server routing documentation.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:146:ruby-shell-backtick-execution","reason":"The path value is a documented delivery mode, not an executable expression.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:147:ruby-shell-backtick-execution","reason":"Inline code identifies a delivery-mode value and its HTTP header; neither is executed as shell text.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:148:ruby-shell-backtick-execution","reason":"The link field is marked with inline Markdown in instructions to preserve server-returned publication identity.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:157:ruby-shell-backtick-execution","reason":"This table row documents the namespace validation expression as data, not backtick execution.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:158:ruby-shell-backtick-execution","reason":"The retention placeholder and empty-value default are literal Markdown table entries.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:159:ruby-shell-backtick-execution","reason":"Inline code documents entry validation that prohibits parent-directory segments.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:160:ruby-shell-backtick-execution","reason":"The table lists enumerated delivery-mode arguments rather than commands.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:161:ruby-shell-backtick-execution","reason":"The code spans describe the password-file placeholder and its empty no-password value.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:164:ruby-shell-backtick-execution","reason":"The type placeholder is an inline label for a value selected from the documented content-type table.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:166:ruby-shell-backtick-execution","reason":"The code spans label the selected path and probe field; the subsequent command passes the path as a quoted argument.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:167:ruby-shell-backtick-execution","reason":"This opens the documented publishing command fence. The command uses the bundled wrapper and the explicit namespace and password workflow.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:169:ruby-shell-backtick-execution","reason":"This is the closing Markdown fence for the file or site publishing command.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:171:ruby-shell-backtick-execution","reason":"The content placeholder is inline Markdown identifying verbatim user-selected text, not executable shell substitution.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:172:ruby-shell-backtick-execution","reason":"This opens the documented text-upload command fence; single-quote escaping is specified earlier to keep content as data.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:174:ruby-shell-backtick-execution","reason":"This closes the Markdown text-upload example, not an executable backtick expression.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:177:ruby-shell-backtick-execution","reason":"The code spans describe staging long text and the generated run identifier.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:180:ruby-shell-backtick-execution","reason":"This opens a Markdown fence around the local text-staging command.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:182:ruby-shell-backtick-execution","reason":"This closes the documented text-staging command fence.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:184:ruby-shell-backtick-execution","reason":"This opens the staged-content publishing example; the wrapper receives the run identifier as data.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:186:ruby-shell-backtick-execution","reason":"This is the closing Markdown fence for the staged-content publishing example.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:188:ruby-shell-backtick-execution","reason":"This opens the cleanup example fence; the wrapper restricts cleanup to a validated private run directory.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:190:ruby-shell-backtick-execution","reason":"This closes the Markdown cleanup example and is not shell command substitution.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:194:ruby-shell-backtick-execution","reason":"The inline code describes the successful publisher status line.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:195:ruby-shell-backtick-execution","reason":"This opens a plain Markdown output template, not an executable command.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:199:ruby-shell-backtick-execution","reason":"This closes the successful-publication output template.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:201:ruby-shell-backtick-execution","reason":"Backticks format the site-success status line and returned file count in prose.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:202:ruby-shell-backtick-execution","reason":"This opens the successful-site output template fence.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:209:ruby-shell-backtick-execution","reason":"This closes the site output template and executes no command.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:214:ruby-shell-backtick-execution","reason":"The public namespace is an inline code value in a warning that unlisted links do not restrict readers.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:218:ruby-shell-backtick-execution","reason":"Inline code names the confirmation status; the instruction requires manifest review before proceeding.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:219:ruby-shell-backtick-execution","reason":"AskUserQuestion is an inline tool name used to request explicit approval of flagged archive entries.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:220:ruby-shell-backtick-execution","reason":"The flags approve only a displayed archive digest after user confirmation; their Markdown backticks do not execute commands.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:221:ruby-shell-backtick-execution","reason":"The digest field is marked as inline code in instructions to request fresh approval whenever archive bytes change.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:226:ruby-shell-backtick-execution","reason":"Inline code describes a failure status line rather than an executable shell expression.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:227:ruby-shell-backtick-execution","reason":"This opens a Markdown failure-message template.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:229:ruby-shell-backtick-execution","reason":"This closes the failure-message template with no executable command.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:235:ruby-shell-backtick-execution","reason":"The HTML extension is inline code in guidance for correcting an unsupported-content server error.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:245:ruby-shell-backtick-execution","reason":"The code spans list bundled script paths and required tools, not commands evaluated through backticks.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:246:ruby-shell-backtick-execution","reason":"Inline code explains a fixed API query parameter and avoids confusing body content with request settings.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:247:ruby-shell-backtick-execution","reason":"Inline code documents protected history placement, project-root selection, and Git exclusion.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:248:ruby-shell-backtick-execution","reason":"Backticks label private temporary directories, environment variables, and tool grants in explanatory prose.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:249:ruby-shell-backtick-execution","reason":"The code span identifies the scoped allowed-tools rule and documents its path limitation; it does not alter audit authority.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:250:ruby-shell-backtick-execution","reason":"The default lifetime and server timestamp fields are inline code labels, not commands.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:251:ruby-shell-backtick-execution","reason":"The ownerLink field is inline code in guidance to use authoritative service metadata for owner operations.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:252:ruby-shell-backtick-execution","reason":"Inline code documents owner-authorized updates and legacy errors; owner tokens are explicitly kept out of chat.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:253:ruby-shell-backtick-execution","reason":"The code spans document owner-authenticated hosting API methods and fields, not shell execution.","verdict":"false_positive","confidence":0.99},{"id":"external_commands:SKILL.md:254:ruby-shell-backtick-execution","reason":"Inline code documents password headers and prohibits putting passwords into link query strings.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:255:ruby-shell-backtick-execution","reason":"The code spans document excluded files, sensitive-name review, and bounded archive validation, not shell substitution.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:256:ruby-shell-backtick-execution","reason":"Inline code identifies the returned link and trailing slash that should be preserved literally.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:23:shell-command-substitution","reason":"The substitution syntax is inside a Markdown list of prohibited command shapes, not an executed shell expression.","verdict":"false_positive","confidence":1},{"id":"external_commands:SKILL.md:23:template-literal-with-command-substitution","reason":"This Markdown code span documents forbidden shell chaining and substitution; it is not a JavaScript template literal.","verdict":"false_positive","confidence":1},{"id":"network:SKILL.md:84:hardcoded-url","reason":"The documented HTTPS endpoint returns public upload limits from the declared hosting service without sending local content.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:198:hardcoded-url","reason":"The account link is explicitly optional publication-management guidance; the workflow does not require sign-in or send credentials there.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:206:hardcoded-url","reason":"This optional account-management link is output guidance, not a concealed upload or credential collection request.","verdict":"false_positive","confidence":0.99},{"id":"network:SKILL.md:265:hardcoded-url","reason":"The Markdown link identifies the advertised hosting service and executes no network request by itself.","verdict":"false_positive","confidence":1},{"id":"network:SKILL.md:266:hardcoded-url","reason":"The GitHub URL is attribution to the plugin repository, not an executable network operation.","verdict":"false_positive","confidence":1},{"id":"filesystem:SKILL.md:197:hidden-file-access","reason":"The output template names the documented owner-token history location, protected by mode 0600 and Git exclusion in the writer.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:SKILL.md:205:hidden-file-access","reason":"The site-success template reports the protected local history path without requesting hidden configuration contents.","verdict":"false_positive","confidence":0.99},{"id":"filesystem:SKILL.md:247:hidden-file-access","reason":"This documents intentional local token storage and project-root discovery; the helper rejects unsafe history paths and tracked files.","verdict":"false_positive","confidence":0.99},{"id":"sensitive:SKILL.md:255:environment-file-access","reason":"The .env reference describes hidden files excluded from directory bundles, not a command to read credentials.","verdict":"false_positive","confidence":1},{"id":"sensitive:SKILL.md:92:sqlite-database-file","reason":"Thumbs.db is listed as a dropped system file, not a SQLite database read.","verdict":"false_positive","confidence":1},{"id":"sensitive:SKILL.md:255:sqlite-database-file","reason":"The database-like filename appears in exclusion documentation; no database connection or content read occurs here.","verdict":"false_positive","confidence":1},{"id":"blocker:SKILL.md:238:system-reconnaissance","reason":"The prose explains server metadata normalization and rejection behavior, not host information gathering.","verdict":"false_positive","confidence":1},{"id":"obfuscation:scripts/brewpage-lib.sh:102:heuristic-very-high-entropy-string-6-03-bits-lik","reason":"The high-entropy string explicitly lists allowed ASCII letters, digits, and periods in a run-identifier rejection pattern, not an encoded payload.","verdict":"false_positive","confidence":1}],"semantic_findings":[{"title":"Directory Packaging Can Follow Replaced Symlinks","severity":"medium","locations":[{"file":"scripts/publish.mjs","line_end":111,"line_start":100},{"file":"scripts/publish.mjs","line_end":224,"line_start":197},{"file":"scripts/brewpage-run.sh","line_end":218,"line_start":203}],"confidence":0.93,"description":"Collection rejects symlinks using lstat, but zip later reopens the selected paths without a stable snapshot. A concurrent writer can replace a selected file or ancestor with a symlink to readable files outside the directory. Archive verification compares names, not source identity, so an unchanged innocuous filename can pass inspection and upload unintended data.","confidence_reasoning":"The code separates lstat validation from zip reads and verifies only archive names afterward. Exploitation requires concurrent write access to the source tree."}],"subject_marketplace_commit_sha":"ffcb29632effb1676658b5f2444e67b91c8c7c87","subject_content_hash":"42d0e03a8294e05dd1f4e385c187eea55c633903299d2bd4eb57f0fd4c44efc1","subject_tree_hash":"7528029d2ba86bc1cf06d29a242c7549655e32b32b7fc8408fa62166e945db63","subject_plugin_path":"skills/kochetkov-ma/brewpage-publish","audit_payload_hash":"1d78c56dd5fd18ff670ebca35fa2f170","confirmed_risk_level":"medium","scanner_version":"3.0.0","policy_version":"skillstore-security-audit-policy-v1","subject":{"marketplaceCommitSha":"ffcb29632effb1676658b5f2444e67b91c8c7c87","contentHash":"42d0e03a8294e05dd1f4e385c187eea55c633903299d2bd4eb57f0fd4c44efc1","treeHash":"7528029d2ba86bc1cf06d29a242c7549655e32b32b7fc8408fa62166e945db63","pluginPath":"skills/kochetkov-ma/brewpage-publish","auditPayloadHash":"1d78c56dd5fd18ff670ebca35fa2f170"},"scannerVersion":"3.0.0","policyVersion":"skillstore-security-audit-policy-v1"},"auditTranslation":null,"localization":{"requestedLocale":"en","contentLocale":"en","availableLocales":["en"],"fallbackToEnglish":false},"attestation":{"availability":"issued","url":"/api/skills/kochetkov-ma-brewpage-publish/audits/8/attestation","status":"superseded"},"trust":{"publicState":"public","auditState":"complete","auditCurrentness":null,"confirmedRiskLevel":"medium","confirmedFindingCount":1,"capabilityReviewCount":0,"needsReviewCount":0,"falsePositiveCount":0,"agentAutoInstallPolicy":"allowed","manualInstallPolicy":"allowed","artifactSignatureState":"available","attestationState":"superseded","verificationState":"not_verified"},"isLatest":false}}