// transparency

Security & Data Handling

ThreatCrush is a scanner people run over their own source, often inside CI, with a token in scope. That is a position of some trust, so this page states plainly what the tool does with your code and your network — and gives you the commands to check every claim on it yourself rather than take our word for it.

Last updated: August 16, 2026

1. threatcrush scan makes no network requests

The scan reads files and writes a report. It opens no sockets — not to us, not to anyone. There is no licence check, no usage ping, no “anonymous statistics” and no update check on the scan path.

You do not have to believe that. Trace the syscalls and count them:

strace -f -qq -e trace=socket,connect,sendto,sendmsg \
  -o net.trace threatcrush scan .

wc -l net.trace     # 0

Validate the method with a control, so an empty file means “no connections” rather than “strace was not watching”:

strace -f -qq -e trace=socket,connect,sendto,sendmsg -o ctl.trace \
  node -e "fetch('https://registry.npmjs.org/')"

wc -l ctl.trace     # 57

2. One flag opts into the network, and it says so

--deps queries api.osv.dev for advisories affecting the versions in your lockfile. It sends package names and versions; it does not send your source. Its own help text is marked (network), and it is off unless you type it.

Everything else in scan is local. Other CLI commands — signing in, publishing modules, managing servers — are network features by definition and are not part of scanning.

3. Telemetry is off unless you turn it on

There is error-reporting code in the package, and we would rather describe it than have you find it. It is gated on an environment variable you set, there is no DSN baked into the build, and it is initialised only by the long-running daemon command — never by scan:

async function initTelemetry(context) {
  const dsn = process.env.SENTRY_DSN;
  if (!dsn) return;              // unset in CI -> returns here
  await loadSentry();            // @sentry/node imported only past this line
  ...
}

With SENTRY_DSN unset — the default everywhere, including every CI runner — the function returns before @sentry/node is even imported. If you do set it, events go to your Sentry project, performance tracing is disabled, and authorization and cookie headers are stripped before send.

4. Your code stays where it is

  • No source, diff, filename or finding is uploaded anywhere by scan.
  • Reports are written where you point them — stdout, a file, a CI job summary, a SARIF artifact.
  • Matched credential material is redacted before it reaches a terminal, a log or a SARIF file. A scanner that prints the secret it found has moved that secret somewhere new, and CI logs are retained.
  • Nothing is written outside the paths you name.

5. Minimum CI permissions

A scan needs to read your code. That is all it needs. If you deliver findings through the job summary and an artifact, the whole workflow runs read-only:

permissions:
  contents: read

Add scopes only for the outputs you actually want, and only the scope each one needs:

  • security-events: write — required only to upload SARIF to the Security tab.
  • pull-requests: write — required only to post findings as a PR comment.

Use pull_request, not pull_request_target: the latter runs with repository secrets in scope against a checkout of untrusted contributor code. We do not ship a workflow that uses it.

6. Supply chain

Pin the version andthe bytes. A version pin says which release to fetch; it does not say the bytes are the ones that release was published with, and the party answering “which version” is the party serving the tarball:

npm pack --pack-destination "$RUNNER_TEMP" @profullstack/threatcrush@0.11.2
got="sha512-$(openssl dgst -sha512 -binary "$RUNNER_TEMP/$name" | openssl base64 -A)"
[ "$got" = "$EXPECTED" ] || exit 1
npm install -g --ignore-scripts "$RUNNER_TEMP/$name"

ThreatCrush declares no install hook of its own, and scan is verified to run from an --ignore-scripts install — so a security gate never opens a shell for its own dependency tree. Published integrity hashes are on npm under dist.integrity.

7. False positives are a bug

A report nobody finishes reading is not a gate. We measure the false-positive rate on real repositories before claiming anything, and rule changes are checked in both directions — a fix has to silence the noise and leave detection unchanged on the public testbed. If ThreatCrush flags something that is not a defect, that is a bug worth an issue.

8. Reporting a vulnerability

Report security issues in ThreatCrush itself privately through GitHub Security Advisories. The source is MIT and public at profullstack/threatcrush — every claim on this page is checkable against it.