Skip to content

fix(threatcrush-scan): drop install scripts and the checkout token - #957

Merged
ralyodio merged 2 commits into
masterfrom
threatcrush-pack-harden
Aug 13, 2026
Merged

fix(threatcrush-scan): drop install scripts and the checkout token#957
ralyodio merged 2 commits into
masterfrom
threatcrush-pack-harden

Conversation

@ralyodio

@ralyodio ralyodio commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

The threatcrush-scan pack fails security review on the repositories it is installed into — a bad look for any workflow, a fatal one for a workflow whose subject is security.

SonarCloud's quality gate on NeptuneHub/AudioMuse-AI#856 failed with a C security rating on new code, both findings on the install line:

githubactions:S6505  Omitting "--ignore-scripts" allows lifecycle scripts to run
                     during package installation.
githubactions:S8543  Using dependencies without locking resolved versions is
                     security-sensitive.

CodeRabbit reached the same conclusion independently on chrisleekr/binance-trading-bot#732, scoring the PR 🟡 Moderate because it "gives an unpinned scanner access to a write-scoped job and retained checkout credentials".

S8543 is already fixed on master#952 pinned threatcrushPackageSpec to 0.11.0 while this branch was open, with a better reason than the one this branch originally carried: the workspace: protocol slip in 0.7.0/0.7.1 is what @latest actually cost. Both sides reached the same default, so the manifest and README here are now byte-identical to master's, which also un-conflicts the branch.

What is left, which master does not have

The job holds pull-requests: write and security-events: write, and it still runs the install with lifecycle scripts enabled and a checkout credential in .git/config.

  • --ignore-scripts on the install (S6505). A lifecycle script is arbitrary code from the dependency tree; the CLI declares no install hook of its own, so this costs nothing it needs.
  • persist-credentials: false on checkout. Nothing in this job pushes, so the token should not sit in the working tree while a network-installed scanner reads a pull request diff.

Verified

--ignore-scripts skips better-sqlite3's native install hook, so this is the change that could plausibly have broken the scan. It does not:

$ npm install -g --prefix ./prefix --ignore-scripts @profullstack/threatcrush@0.11.0
added 208 packages in 10s
$ ./prefix/bin/threatcrush --version
0.11.0
$ ./prefix/bin/threatcrush scan ./fixture
  CRITICAL  AWS Access Key         app.js:1  secret-aws-access-key · CWE-798
  [HIGH]    Hardcoded Credential   app.js:1  secret-generic-credential · CWE-798
  [MEDIUM]  dynamic code execution app.js:3  js-dynamic-code-execution · CWE-95
  3 issue(s) found across 1 files

Both YAML files still parse, and the rendered workflow carries the changes through:

      - uses: actions/checkout@v4
          persist-credentials: false
            if npm install -g --ignore-scripts "@profullstack/threatcrush@0.11.0"; then

Rendered into AudioMuse-AI#856, SonarCloud went from 2 MAJOR vulnerabilities to 0 open issues and the gate passes.

Consumer side is profullstack/threatcrush#121; wider discussion of the 0-for-6 first batch is profullstack/threatcrush#120.

🤖 Generated with Claude Code

…kout token

SonarQube fails the pack's own workflow on a repository that runs it,
which is a poor look for a security scan:

  githubactions:S6505  omitting --ignore-scripts allows lifecycle scripts
                       to run during package installation
  githubactions:S8543  using dependencies without locking resolved versions

Both are fair. The job holds `pull-requests: write` and
`security-events: write`, and it was installing a floating `@latest` with
lifecycle scripts enabled — so an npm publish by anyone in the CLI's
dependency tree ran arbitrary code inside a write-scoped job on a
stranger's runner, with a checkout credential still in .git/config.

- install with --ignore-scripts. The CLI declares no install hook, and
  `scan` was verified to run correctly from an --ignore-scripts install.
- default to an exact version rather than @latest. Whoever installs the
  pack renders the version current at the time; the repository upgrades
  when it decides to.
- persist-credentials: false on checkout. Nothing in this job pushes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#952 pinned threatcrushPackageSpec to 0.11.0 and bumped the manifest to
1.2.0 while this branch was open, with a better reason than the one here:
the `workspace:` protocol slip in 0.7.0/0.7.1 is what @latest actually
cost, and that is worth more in the file than a rule number.

Both sides reached the same default, so the manifest and README go back to
master's wording verbatim — which also un-conflicts the branch. What is
left is the half master still does not have: --ignore-scripts on the
install, and persist-credentials: false on checkout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

310 finding(s)

HIGH/CRITICAL: 24 | MEDIUM: 50 | LOW: 236

Severity Rule Location
HIGH secret-generic-api-key packages/affiliates/sovrn/src/index.ts:28
HIGH js-nosql-injection packages/ai/amazon-bedrock/src/index.test.ts:121
HIGH secret-generic-credential packages/ai/amazon-bedrock/src/index.ts:9
HIGH secret-generic-credential packages/ai/amazon-bedrock/src/index.ts:10
HIGH secret-generic-credential packages/ai/amazon-bedrock/src/index.ts:11
HIGH js-host-header-trust packages/bots/wechat/src/index.ts:405
HIGH secret-generic-credential packages/bridges/matrix/src/index.ts:58
HIGH secret-generic-credential packages/bridges/matrix/src/index.ts:59
HIGH secret-generic-credential packages/captcha/captchasolver/src/index.ts:34
HIGH secret-generic-credential packages/cli/src/commands/secrets.ts:176
HIGH secret-generic-credential packages/cloud/linode/src/index.ts:15
HIGH secret-generic-credential packages/observability/sentry/src/index.ts:15
HIGH secret-generic-credential packages/outreach/producthunt/src/index.ts:103
HIGH secret-generic-credential packages/promo/posthog/src/index.ts:23
HIGH secret-generic-credential packages/security/snyk/src/index.ts:26
HIGH secret-generic-credential packages/social/hashnode/src/index.ts:4
HIGH secret-generic-credential packages/social/linkedin/src/index.ts:3
HIGH secret-generic-credential packages/social/linkedin/src/index.ts:4
HIGH secret-generic-credential packages/social/medium/src/index.ts:4
HIGH secret-generic-credential packages/social/snapchat/src/index.ts:5
HIGH secret-generic-credential packages/social/tiktok/src/index.ts:5
HIGH secret-generic-credential packages/targets/registry-ans/src/index.ts:49
HIGH secret-generic-credential sites/sh1pt.com/supabase/config.toml:303
HIGH secret-generic-credential sites/sh1pt.com/supabase/config.toml:335
MEDIUM redos-nested-quantifier packages/actions-fleet-core/src/action-pack/schema.ts:3
MEDIUM insecure-temp-file packages/agent-providers/opencode/src/__tests__/opencode.test.ts:19
MEDIUM insecure-temp-file packages/agent-providers/opencode/src/__tests__/opencode.test.ts:42
MEDIUM insecure-temp-file packages/agent-providers/opencode/src/__tests__/opencode.test.ts:45
MEDIUM insecure-temp-file packages/bridges/signal/src/index.test.ts:92
MEDIUM insecure-temp-file packages/bridges/signal/src/index.test.ts:118
MEDIUM insecure-temp-file packages/cli/src/input.test.ts:84
MEDIUM redos-nested-quantifier packages/core/src/setup-helpers.ts:583
MEDIUM insecure-temp-file packages/core/src/testing/harness.ts:15
MEDIUM insecure-temp-file packages/core/src/testing/harness.ts:16
MEDIUM insecure-temp-file packages/core/src/testing/harness.ts:30
MEDIUM insecure-temp-file packages/core/src/testing/harness.ts:42
MEDIUM insecure-temp-file packages/merch/printful/src/index.test.ts:9
MEDIUM insecure-temp-file packages/merch/printify/src/index.test.ts:11
MEDIUM insecure-temp-file packages/policy/src/linter.test.ts:8
MEDIUM redos-nested-quantifier packages/policy/src/rules/bundle-id.ts:3
MEDIUM insecure-temp-file packages/secrets/env-updater/src/index.test.ts:106
MEDIUM insecure-temp-file packages/social/facebook/src/index.test.ts:95
MEDIUM insecure-temp-file packages/social/instagram/src/index.test.ts:177
MEDIUM insecure-temp-file packages/social/pinterest/src/index.test.ts:91
MEDIUM insecure-temp-file packages/social/pinterest/src/index.test.ts:146
MEDIUM insecure-temp-file packages/social/threads/src/index.test.ts:108
MEDIUM insecure-temp-file packages/social/vimeo/src/index.test.ts:137
MEDIUM insecure-temp-file packages/social/x/src/index.test.ts:72
MEDIUM insecure-temp-file packages/social/x/src/index.test.ts:116
MEDIUM insecure-temp-file packages/targets/browser-safari/src/index.test.ts:22

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

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio ralyodio changed the title fix(threatcrush-scan): pin the CLI, drop install scripts and the checkout token fix(threatcrush-scan): drop install scripts and the checkout token Aug 13, 2026
@ralyodio
ralyodio merged commit 9b87116 into master Aug 13, 2026
8 checks passed
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.

1 participant