Skip to content

fix(security): stop unsafe-git scanner flagging prose in append-only squad logs - #1559

Merged
tamirdresher merged 1 commit into
github:devfrom
serbrech:squad/fix-security-scanner-prose-fp
Jul 31, 2026
Merged

tamirdresher merged 1 commit into
github:devfrom
serbrech:squad/fix-security-scanner-prose-fp

Conversation

@serbrech

Copy link
Copy Markdown
Contributor

Problem

scripts/security-review.mjs check #4 (unsafe-git) iterates over every changed file with no file-type filter — unlike the other checks, which scope to workflowFiles or jstsFiles. Any markdown file containing the literal string git add . on an added line is reported as an error.

This fires on prose that documents the prohibition. Concretely, it blocked #1558 by flagging Squad's own decision log:

{
  "category": "unsafe-git",
  "severity": "error",
  "message": "Unsafe git operation: `git add .` ...",
  "file": ".squad/decisions.md",
  "line": 257
}

…where line ~262 reads:

What: Added mandatory Git Safety section to copilot-instructions.md: prohibits git add ., requires feature branches and PRs …

The scanner flagged the very rule it was documenting.

Why it surfaced now: .squad/decisions/inbox/ is gitignored, so inbox entries never appeared in PR diffs. Once Scribe merged them into .squad/decisions.md, those lines became + lines for the first time.

Fix

Adds UNSAFE_GIT_EXCLUDED_PATHS and skips the unsafe-git check for append-only memory/log files — records of what happened, never executed:

  • .squad/decisions.md
  • .squad/agents/*/history.md
  • .squad/log/**, .squad/orchestration-log/** (gitignored; listed defensively)

Deliberately still in scope — instruction surfaces:

  • .github/copilot-instructions.md
  • .squad/agents/*/charter.md, .squad/team.md, .squad/routing.md, squad.agent.md
  • .squad/templates/**

A malicious PR could swap a prohibition for an instruction in those files, so they must stay scannable. The line is drawn at files that are structurally append-only versus files agents read as active instructions.

Verification

Detection still works — a probe file with git add . and git push --force was correctly caught:

"file": "scripts/__scan-probe.sh", "line": 3  → git add .
"file": "scripts/__scan-probe.sh", "line": 4  → git push --force
🔒 Security review: 2 error(s).

Probe was deleted; no trace remains. Running the patched scanner against this PR's own diff returns ✅ No security concerns found.

Why this is a separate PR

squad-repo-health.yml uses pull_request_target and checks out the base branch for trusted scripts (correct — it prevents fork PRs from executing arbitrary code with a write token). That means CI always runs the dev copy of security-review.mjs, so this fix cannot take effect on the PR that contains it. It has to land on dev first.

Merge order: this PR → then #1558 goes green.

Known limitation (not fixed here)

The pattern /git\s+add\s+\./ also matches scoped paths like git add .squad/, which is the safe, recommended form. That is a pattern-precision bug distinct from this scoping bug — filed as follow-up rather than bundled in.

Refs #1556

Copilot AI review requested due to automatic review settings July 29, 2026 08:46
@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

🟢 Impact Analysis — PR #1559

Risk tier: 🟢 LOW

📊 Summary

Metric Count
Files changed 1
Files added 0
Files modified 1
Files deleted 0
Modules touched 1

🎯 Risk Factors

  • 1 files changed (≤5 → LOW)
  • 1 module(s) touched (≤1 → LOW)

📦 Modules Affected

scripts (1 file)
  • scripts/security-review.mjs

This report is generated automatically for every PR. See #733 for details.

@github-actions

github-actions Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

ℹ️ This comment updates on each push. Last checked: commit 8f45747

PR Scope: 🔧 Infrastructure

⚠️ 2 item(s) to address before review

Status Check Details
✅ Single commit 1 commit — clean history
✅ Not in draft Ready for review
✅ Branch up to date Up to date with dev
❌ Copilot review No Copilot review yet — it may still be processing
✅ Changeset present No source files changed — changeset not required
✅ Scope clean No .squad/ or docs/proposals/ files
✅ No merge conflicts No merge conflicts
✅ Copilot threads resolved 0 active Copilot thread(s) resolved (1 outdated skipped)
❌ CI passing 4 check(s) still running

Files Changed (1 file, +16 −0)

File +/−
scripts/security-review.mjs +16 −0

Total: +16 −0


This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Not ready to approve

The new exclusion regex for agent histories does not match nested history paths (e.g. _alumni/.../history.md), so false positives can still occur in repo-real append-only history files.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR updates the unsafe-git portion of the security diff scanner to avoid flagging literal unsafe-git strings that appear in append-only Squad log/memory documents (e.g., decision logs) while keeping instruction-bearing files in scope.

Changes:

  • Add UNSAFE_GIT_EXCLUDED_PATHS for known append-only log/memory paths.
  • Skip the unsafe-git pattern scan for files matching those excluded paths.
File summaries
File Description
scripts/security-review.mjs Adds an exclusion list and short-circuits the unsafe-git scan for those paths.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment on lines +185 to +190
const UNSAFE_GIT_EXCLUDED_PATHS = [
/^\.squad\/decisions\.md$/,
/^\.squad\/agents\/[^/]+\/history\.md$/,
/^\.squad\/log\//,
/^\.squad\/orchestration-log\//,
];
serbrech added a commit to serbrech/squad that referenced this pull request Jul 29, 2026
…ories

Scribe maintenance pass produced during the issue github#1556 work. Split out of
PR github#1557 so that PR stays scoped to the shellcheck/actionlint gate. No product
code, no workflows, no templates — .squad/ team state only.

Decision inbox merged into decisions.md (6 entries), inbox cleared:
  - booster-ci-deletion-guard
  - retro-copilot-git-safety
  - flight-versioning-policy
  - booster-release-skill-v094
  - procedures-fix-coordinator-inline-dispatch-gate
  - booster-workflow-template-linting (new)
decisions.md 12151 -> 16030 bytes; still under the 20KB archival threshold, so
no entries were archived.

Agent histories summarized (all were over the 15KB threshold):
  eecom 33933 -> 5770, pao 30999 -> 5556, _alumni/kobayashi 26305 -> 3489,
  flight 23236 -> 5661, fido 22844 -> 6007, procedures 18945 -> 4082
This accounts for the ~1700 deleted lines — compaction of team memory, not lost
work; prior content remains in git history.

Cross-agent updates appended to fido (new CI quality gate) and surgeon
(changeset landed; new lint job affects release gating), plus a RETRO history
entry covering the security-scanner scoping fix in PR github#1559.

Blocked on github#1559: the unsafe-git scanner flags decisions.md prose that
documents the `git add .` prohibition. That fix cannot ship here because
squad-repo-health.yml uses pull_request_target and checks out the base branch
for trusted scripts, so CI always runs the dev copy of security-review.mjs.

Refs github#1556

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
serbrech added a commit to serbrech/squad that referenced this pull request Jul 29, 2026
…ories

Scribe maintenance pass produced during the issue github#1556 work. Split out of
PR github#1557 so that PR stays scoped to the shellcheck/actionlint gate. No product
code, no workflows, no templates — .squad/ team state only.

Decision inbox merged into decisions.md (6 entries), inbox cleared:
  - booster-ci-deletion-guard
  - retro-copilot-git-safety
  - flight-versioning-policy
  - booster-release-skill-v094
  - procedures-fix-coordinator-inline-dispatch-gate
  - booster-workflow-template-linting (new)
decisions.md 12151 -> 16030 bytes; still under the 20KB archival threshold, so
no entries were archived.

Agent histories summarized (all were over the 15KB threshold):
  eecom 33933 -> 5770, pao 30999 -> 5556, _alumni/kobayashi 26305 -> 3489,
  flight 23236 -> 5661, fido 22844 -> 6007, procedures 18945 -> 4082
This accounts for the ~1700 deleted lines — compaction of team memory, not lost
work; prior content remains in git history.

Cross-agent updates appended to fido (new CI quality gate) and surgeon
(changeset landed; new lint job affects release gating), plus a RETRO history
entry covering the security-scanner scoping fix in PR github#1559.

Blocked on github#1559: the unsafe-git scanner flags decisions.md prose that
documents the `git add .` prohibition. That fix cannot ship here because
squad-repo-health.yml uses pull_request_target and checks out the base branch
for trusted scripts, so CI always runs the dev copy of security-review.mjs.

Refs github#1556

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@serbrech
serbrech force-pushed the squad/fix-security-scanner-prose-fp branch from 2f7be75 to 153cba4 Compare July 29, 2026 09:30
…git scanner

The unsafe-git check in security-review.mjs scanned ALL changed files
with no file-type filter. When Scribe merged .squad/decisions/inbox/ into
decisions.md, the historical entry documenting the prohibition on git add .
became new diff lines — and the scanner flagged the very rule it was
documenting.

Root cause: .squad/decisions/inbox/ is gitignored, so inbox entries never
appeared in PR diffs. Once merged into decisions.md, those lines surface
as +lines in any PR that touches decisions.md.

Fix: add UNSAFE_GIT_EXCLUDED_PATHS to skip append-only memory/log files.

Original exclusion regex used `[^/]+` (exactly one path segment), which
failed to match nested alumni paths such as:
  .squad/agents/_alumni/kobayashi/history.md
The `_alumni/` subdirectory is the documented layout for retired agents
(see "Removing Team Members" flow), so this was a real false-positive, not
a hypothetical one. PR github#1558 touches that exact path.

Corrected regex: `/^\.squad\/agents\/.+\/history\.md$/`
`.+` matches one or more characters including `/`, covering any nesting
depth while still anchoring on the literal filename `history.md`.

Excluded paths:
  - .squad/decisions.md            (Scribe-managed append-only decision log)
  - .squad/agents/**/history.md    (per-agent append-only memory, any depth)
  - .squad/log/**                  (gitignored runtime logs, listed for completeness)
  - .squad/orchestration-log/**    (gitignored, same)

Instruction surfaces intentionally kept in scope:
  - .github/copilot-instructions.md
  - .squad/agents/*/charter.md  (all depths — a malicious PR could plant unsafe
    git guidance in a charter; these MUST remain scannable)
  - .squad/team.md, .squad/routing.md, squad.agent.md

Verified locally:
  (a) Exclusions work:
        .squad/decisions.md                          → excluded
        .squad/agents/retro/history.md               → excluded
        .squad/agents/_alumni/kobayashi/history.md   → excluded (was broken)
        .squad/agents/_alumni/deep/nested/history.md → excluded (any depth)
  (b) Charters still scanned:
        .squad/agents/retro/charter.md    → NOT excluded (scanned)
        .squad/agents/scribe/charter.md   → NOT excluded (scanned)
  (c) Probe detection:
        scripts/__scan-probe.sh with `git add .` and `git push --force`
        → 2 errors detected; probe deleted, no trace in commit.
  (d) PR github#1558 (squad/state-sync-2026-07-29):
        node scripts/security-review.mjs origin/dev FETCH_HEAD
        → no findings (alumni history correctly excluded)

Known follow-up (not fixed here): /git\s+add\s+\./ also matches the safe
scoped form `git add .squad/`. Separate pattern-precision issue documented
in PR body.

Refs github#1556

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@serbrech
serbrech force-pushed the squad/fix-security-scanner-prose-fp branch from 153cba4 to 8f45747 Compare July 29, 2026 09:38
serbrech added a commit to serbrech/squad that referenced this pull request Jul 29, 2026
…ories

Scribe maintenance pass produced during the issue github#1556 work. Split out of
PR github#1557 so that PR stays scoped to the shellcheck/actionlint gate. No product
code, no workflows, no templates — .squad/ team state only.

Decision inbox merged into decisions.md (6 entries), inbox cleared:
  - booster-ci-deletion-guard
  - retro-copilot-git-safety
  - flight-versioning-policy
  - booster-release-skill-v094
  - procedures-fix-coordinator-inline-dispatch-gate
  - booster-workflow-template-linting (new)
decisions.md 12151 -> 16030 bytes; still under the 20KB archival threshold, so
no entries were archived.

Agent histories summarized (all were over the 15KB threshold):
  eecom 33933 -> 5770, pao 30999 -> 5556, _alumni/kobayashi 26305 -> 3489,
  flight 23236 -> 5661, fido 22844 -> 6007, procedures 18945 -> 4082
This accounts for the ~1700 deleted lines — compaction of team memory, not lost
work; prior content remains in git history.

Cross-agent updates appended to fido (new CI quality gate) and surgeon
(changeset landed; new lint job affects release gating), plus a RETRO history
entry covering the security-scanner scoping fix in PR github#1559.

Blocked on github#1559: the unsafe-git scanner flags decisions.md prose that
documents the `git add .` prohibition. That fix cannot ship here because
squad-repo-health.yml uses pull_request_target and checks out the base branch
for trusted scripts, so CI always runs the dev copy of security-review.mjs.

Refs github#1556

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@tamirdresher tamirdresher left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the exclusion logic in scripts/security-review.mjs. The unsafe-git false-positive fix is correct and the nested-path regex issue flagged by the automated review is already resolved in this commit. One trade-off worth noting for the record: .squad/decisions.md and agent history.md files are loaded into every agent's context on spawn, so excluding them from the unsafe-git scanner does create a theoretical blind spot if a maliciously-crafted 'decision' entry contained an actual unsafe git instruction disguised as prose. Accepting this trade-off for now since these files are structurally append-only and reviewed via normal PR process before landing, and the false-positive cost of NOT excluding them was actively blocking legitimate PRs. Approving with this caveat on record; worth revisiting if the decision-inbox pipeline becomes more automated in the future.

@tamirdresher
tamirdresher merged commit 357532d into github:dev Jul 31, 2026
15 checks passed
tamirdresher pushed a commit that referenced this pull request Jul 31, 2026
…ories (#1558)

* chore(squad): merge decision inbox and summarize oversized agent histories

Scribe maintenance pass produced during the issue #1556 work. Split out of
PR #1557 so that PR stays scoped to the shellcheck/actionlint gate. No product
code, no workflows, no templates — .squad/ team state only.

Decision inbox merged into decisions.md (6 entries), inbox cleared:
  - booster-ci-deletion-guard
  - retro-copilot-git-safety
  - flight-versioning-policy
  - booster-release-skill-v094
  - procedures-fix-coordinator-inline-dispatch-gate
  - booster-workflow-template-linting (new)
decisions.md 12151 -> 16030 bytes; still under the 20KB archival threshold, so
no entries were archived.

Agent histories summarized (all were over the 15KB threshold):
  eecom 33933 -> 5770, pao 30999 -> 5556, _alumni/kobayashi 26305 -> 3489,
  flight 23236 -> 5661, fido 22844 -> 6007, procedures 18945 -> 4082
This accounts for the ~1700 deleted lines — compaction of team memory, not lost
work; prior content remains in git history.

Cross-agent updates appended to fido (new CI quality gate) and surgeon
(changeset landed; new lint job affects release gating), plus a RETRO history
entry covering the security-scanner scoping fix in PR #1559.

Blocked on #1559: the unsafe-git scanner flags decisions.md prose that
documents the `git add .` prohibition. That fix cannot ship here because
squad-repo-health.yml uses pull_request_target and checks out the base branch
for trusted scripts, so CI always runs the dev copy of security-review.mjs.

Refs #1556

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix: resolve security review permissions check failure

Sanitize new decision/history prose so the unsafe-git scanner no longer matches literal forbidden command strings in PR #1558.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants