Skip to content

FUSI-052: Reviewer finding (2026-09-26 tick, Workflow Merger - #35

Open
timoteo7 wants to merge 4 commits into
mainfrom
fusion/fusi-052
Open

timoteo7 wants to merge 4 commits into
mainfrom
fusion/fusi-052

Conversation

@timoteo7

Copy link
Copy Markdown
Owner

Automated PR for FUSI-052.

Reviewer finding (2026-09-26 tick, Workflow Merger agent-8de5231c): the repository's security gate is STRUCTURALLY UNABLE TO FAIL ON A FINDING. .github/workflows/threatcrush-scan.yml:142 hardcodes FAIL_ON=\"\", and the only consumer (:148) appends --fail-on only when that string is non-empty. There is no env override, no workflow_dispatch input, no default. ThreatCrush therefore never receives a severity threshold, per the workflow's own comment at :191 it can never return exit 1, and the 1) arm at :190-193 that exists specifically to fail the job on findings is unreachable. Every other exit path in that step fails on a broken SCAN, not on a detected problem. A PR with a live credential in it and a PR with a clean tree both conclude success.

This is not a style preference; it is the workflow contradicting its own recorded intent. The FNXC comment immediately above the unreachable arm says: "a gate that records the finding and then lets the job pass is not a gate." The code below that comment does exactly the thing the comment forbids.

Live proof, measured 2026-09-26 (read-only, no PR mutation)

PR Runfusion/Fusion#3664 (head 71b2cf4c7c2c8429a23074ef7a8e3642a3892f31): check run ThreatCrush conclusion = success (run 108348368205), while the PR simultaneously carries 6 unresolved github-advanced-security review threads, all titled ThreatCrush / predictable temporary file path (CWE-377) — alert IDs 4882, 4883, 4884 and 5442, 5443, 5444. Green gate, six open security findings, same commit.

The six threads all land on one file, docs/solutions/test-failures/main-full-suite-census-2026-09-25.md, at lines 357, 358 and 522. Those lines are markdown table rows quoting vitest assertion text verbatim, e.g. line 522 contains expected '/tmp/fusion-test-workers-xiv9Xv/redir…' to contain '.worktrees'. The scanner reads a documentation table cell as a temporary-file path in code. The PR author already reached that conclusion independently: commit e2ccb9fe3 on the fork is titled docs(FUSI-020): redact the Postgres URL that tripped ThreatCrush and its message states "The three CWE-377 hits are pre-existing false positives: shard-log assertion strings quoted verbatim in the census rows."

That same commit is the second-order cost. A real postgresql://postgres:***@localhost:5432 URL in prose DID trip a HIGH Database URL with credentials finding, and a human spent a commit hand-redacting it. The scanner's signal on prose is unreliable in both directions, and neither direction is actionable because the gate cannot fail either way.

Why the scan produces this class at all

SCAN_PATH=\".\" (:143) with a default actions/checkout scans the whole tree at the merge ref, not the diff. So a PR that adds a documentation file inherits scanner verdicts on prose it merely quotes. The workflow's own failure text at :180 calls the artifact "this diff", which is not what was scanned.

Blast radius beyond Runfusion#3664

Swept all 100 open PRs' unresolved threads by author. github-advanced-security has unresolved threads on 6 PRs: Runfusion#3664 (6), Runfusion#3647 (3), Runfusion#3627 (3), Runfusion#3632 (3), Runfusion#3429 (3), Runfusion#3428 (2), Runfusion#3432 (2). Every one of those threads lands on a file that PR actually modifies — checked individually, not inferred. So the alerting is at least correctly scoped to touched files; the defect is purely that none of them can ever block.

What This Delivers

The security check either blocks on a real finding or stops claiming to be a gate. A red X on a committed credential has to be possible, and a green check on a PR with six open HIGH/MEDIUM findings must not read as "security clean" to the humans and agents making merge decisions.

Before → After Transformation

  • Before: FAIL_ON=\"\" is a literal with no override path, so --fail-on is never passed and the status=findings arm at :190 is dead code.
  • Before: the workflow's own FNXC comment states that letting findings pass "is not a gate", immediately above the line that lets them pass.
  • Before: ThreatCrush: success on a PR head is reported as a green security signal on 41 open PRs, while up to 6 unresolved security threads sit on the same head.
  • Before: SCAN_PATH=\".\" scans the merge ref, not the diff, so prose that quotes an assertion string is judged as code, and the failure text at :180 misnames the scope.
  • After: a severity threshold is configured from a real input, and the status=findings arm is reachable — or the arm is deleted and the job is honestly named as advisory, so no merge decision reads it as a gate.
  • After: a PR carrying a live credential in any file can turn the check red.
  • After: the scan's scope matches what the workflow says it scans, and the report says which.

Steps (bounded)

  1. Decide the policy before touching the YAML: does this repository want a BLOCKING credential gate or an advisory scan? Both are defensible; the current state — a job named "Scan for credentials" that cannot fail on a credential — is neither. Record the decision in an FNXC comment at the decision site.
  2. If blocking: give FAIL_ON a real source (repo variable with a documented default, or a hardcoded severity) and prove the status=findings arm is reachable. A workflow edit that leaves the arm unreachable has fixed nothing.
  3. If advisory: delete the unreachable arm and the misleading comment, and rename the check so it cannot be read as a gate by a merge decision.
  4. Settle the scope question: SCAN_PATH=\".\" scans the merge ref, not the diff. Decide whether that is intended, and make the report text at :180 describe the real scope either way.
  5. Do NOT dismiss or resolve the 6 existing github-advanced-security alerts on docs(FUSI-020): name and classify the main Full Suite red streak (1762 runs, 233 cases, 0 flakes) Runfusion/Fusion#3664, and do not touch any PR. Confirming the CWE-377 verdicts there are false positives is worth recording, but that is a human call on the Security tab — the code-scanning/alerts REST endpoint returns 403 for this identity, so the alerts could not be independently read back this tick and their state is unverified beyond the thread titles and locations.

Constraints

  • Read-only with respect to every PR. No push, no comment, no thread resolution, no alert dismissal, no label change.
  • The shared primary checkout is read-only for this card: no stash, reset, clean, switch, add, or restore. Per project memory, git push is blocklisted in this environment; a PR-bound implementation of this card needs the gh api route.
  • Do not widen this into the CWE-377 false-positive tuning problem. Whether the scanner should ignore docs/** is a separate decision from whether the gate can fail; a card that does both will do neither.
  • Do not "fix" the false positives by redacting more prose. e2ccb9fe3 already shows where that road ends: every assertion string in a 545-line failure census is a candidate for the next redaction commit.

Non-duplication (checked 2026-09-26 against open cards)

  • FUSI-003 (todo) — "Extração de threads não resolvidas de todos os bots". A data-extraction task about reading bot state. It consumes the threads this card explains; it does not own the gate that produces them.
  • FUSI-002 / FUSI-001 — score and discovery extraction over the same PR set. No gate ownership.
  • FUSI-051 (Workflow Executor, todo) — the merge-gate livelock on FUSI-020. It cites e2ccb9fe3 only as evidence that the card's branch diverges from the live PR branch. Different layer: that card is about the engine's review-fingerprint loop, not about whether the security check can fail. Neither card mentions threatcrush-scan.yml.
  • Searched threatcrush, SARIF, security-events, fail-on, code scanning, and CWE-377 across the board: no card names this workflow.

Out of scope (operator holds, untouched)

#3664 and every other open PR sit at reviewDecision: REVIEW_REQUIRED with a required human approving review. The 4 ungoverned chain PRs (Runfusion#3430-Runfusion#3433, reviewDecision: null) remain FUSI-038's hazard. Neither is touched here. Runfusion#3664's own merge gate is separately stuck in the FUSI-051 livelock.

timoteo7 and others added 4 commits September 29, 2026 09:14
…n threshold

Record the blocking-gate policy as an FNXC block and resolve FAIL_ON from an
optional repository variable with a trim-then-default of `high`. The previous
hardcoded empty literal made --fail-on unreachable and the findings arm dead.

Co-authored-by: Fusion <noreply@runfusion.ai>
Fusion-Task-Id: FUSI-052
… by executing the step body

Extract the real Scan step body from the workflow, run it under bash with a
contract-faithful threatcrush stub, and assert on exit code plus status= output.
The stub returns exit 1 only when --fail-on was actually received, so the suite
is red on the pre-fix workflow and green after — verified by reverting FAIL_ON
to the bare literal (8/9 fail) and restoring.

Co-authored-by: Fusion <noreply@runfusion.ai>
Fusion-Task-Id: FUSI-052
…lly applied

Replace the three user-facing "this diff" strings with the real scope (full
working tree at the merge ref), add a header scope line, and thread the resolved
threshold from the scan step through to the report so a red job states the
severity boundary that produced it.

Co-authored-by: Fusion <noreply@runfusion.ai>
Fusion-Task-Id: FUSI-052
Co-authored-by: Fusion <noreply@runfusion.ai>
Fusion-Task-Id: FUSI-052
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

Scanned: the full working tree at the merge ref (not just the changed files).
The job fails when a finding at or above high severity is present.
4590 finding(s)

HIGH/CRITICAL: 44 | MEDIUM: 4035 | LOW: 511

Severity Rule Location
HIGH secret-database-url .github/workflows/full-suite.yml:55
HIGH secret-generic-credential .github/workflows/full-suite.yml:56
HIGH secret-database-url .github/workflows/full-suite.yml:284
HIGH secret-generic-credential .github/workflows/full-suite.yml:285
HIGH secret-database-url .github/workflows/full-suite.yml:324
HIGH secret-generic-credential .github/workflows/full-suite.yml:325
HIGH secret-database-url .github/workflows/pr-checks.yml:221
HIGH secret-generic-credential .github/workflows/pr-checks.yml:222
HIGH secret-generic-credential .github/workflows/release.yml:522
HIGH secret-generic-credential .github/workflows/release.yml:524
HIGH secret-generic-credential .github/workflows/test-release.yml:445
HIGH secret-generic-credential .github/workflows/test-release.yml:447
HIGH secret-database-url .github/workflows/threatcrush-scan.yml:157
HIGH secret-generic-credential docs/cli-reference.md:80
HIGH secret-generic-credential docs/signals-connectors.md:34
HIGH secret-generic-credential docs/signals-connectors.md:77
HIGH secret-generic-credential docs/signals-connectors.md:94
HIGH secret-generic-credential docs/signals-connectors.md:117
HIGH secret-generic-credential docs/signals-connectors.md:159
HIGH secret-generic-credential packages/cli/STANDALONE.md:71
HIGH secret-database-url packages/core/src/postgres/credential-redact.ts:12
HIGH secret-database-url packages/core/src/postgres/credential-redact.ts:30
HIGH secret-database-url packages/core/src/postgres/credential-redact.ts:31
HIGH secret-database-url packages/core/src/postgres/credential-redact.ts:102
HIGH secret-database-url packages/core/src/postgres/credential-redact.ts:103
HIGH secret-generic-credential packages/core/src/postgres/embedded-lifecycle.ts:843
HIGH secret-database-url packages/core/src/postgres/embedded-lifecycle.ts:1574
HIGH secret-database-url packages/core/src/postgres/pg-backup.ts:756
HIGH js-ssrf-outbound-request packages/dashboard/app/public/sw.js:651
HIGH js-ssrf-outbound-request packages/dashboard/app/public/sw.js:727
HIGH js-host-header-trust packages/dashboard/src/cli-session-ws.ts:81
HIGH js-host-header-trust packages/dashboard/src/cli-session-ws.ts:115
HIGH js-ssrf-outbound-request packages/dashboard/src/routes.ts:1858
HIGH js-host-header-trust packages/dashboard/src/server.ts:2689
HIGH js-host-header-trust packages/dashboard/src/server.ts:2714
HIGH js-host-header-trust packages/dashboard/src/server.ts:3025
HIGH js-host-header-trust packages/dashboard/src/server.ts:3193
HIGH secret-slack-webhook plugins/examples/fusion-plugin-notification/README.md:46
HIGH secret-database-url scripts/pg-test-server.mjs:200
HIGH secret-database-url scripts/pg-test-server.mjs:231
HIGH secret-database-url scripts/pg-test-server.mjs:241
HIGH secret-generic-credential scripts/sync-fusion-skill-tools.mjs:550
HIGH secret-generic-credential scripts/verify-windows-elevated-restricted.mjs:81
HIGH secret-generic-credential scripts/verify-windows-encoding-recovery.mjs:41
MEDIUM redos-nested-quantifier docs/agents.md:1710
MEDIUM insecure-temp-file docs/solutions/test-failures/main-full-suite-census-2026-09-25.md:376
MEDIUM insecure-temp-file docs/solutions/test-failures/main-full-suite-census-2026-09-25.md:377
MEDIUM insecure-temp-file docs/solutions/test-failures/main-full-suite-census-2026-09-25.md:541
MEDIUM insecure-temp-file packages/cli/src/__tests__/bin.test.ts:136
MEDIUM insecure-temp-file packages/cli/src/__tests__/dev-with-memory-lib.test.ts:33

…and 4540 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

# PR and a clean tree both concluded "success".
#
# Threshold `high` is measured, not taste: on the pinned
# @profullstack/threatcrush@0.11.0 a committed `postgresql://user:pass@host`
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.

2 participants