> ## Documentation Index
> Fetch the complete documentation index at: https://glide-9da73dea.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# @glideco/secrets-scan

> Denylist + entropy secret scanner. Catches JWTs, API keys, PEM blocks, and private keys before they leak to logs or audit streams.

Two-pass secret scanner. The first pass matches known-format secrets against
curated regexes; the second catches high-entropy opaque blobs that didn't
match any pattern. Used in the Glide Trust Console event ingestion pipeline
to redact `ReasoningStepEvent.excerpt` before persistence, and as a CI gate
on every PR via `scripts/scan-secrets.mjs`.

## Install

```bash theme={null}
npm install @glideco/secrets-scan
```

[npmjs.com/package/@glideco/secrets-scan](https://www.npmjs.com/package/@glideco/secrets-scan)

## What it catches

| Kind                 | Detection method   | Example pattern                                       |
| -------------------- | ------------------ | ----------------------------------------------------- |
| `jwt`                | denylist           | `eyJ…` three-part structure                           |
| `pem-block`          | denylist           | `-----BEGIN … PRIVATE KEY-----`                       |
| `aws-access-key`     | denylist           | `AKIA[A-Z0-9]{16}`                                    |
| `aws-secret-key`     | denylist + context | contextual prefix required (`aws_secret_access_key=`) |
| `stripe-key`         | denylist           | `sk_live_`, `pk_test_`, `rk_live_` + 20+ chars        |
| `github-pat`         | denylist           | `ghp_`, `gho_`, `ghu_`, `ghs_`, `ghr_` + 30+ chars    |
| `slack-token`        | denylist           | `xox[abprs]-`                                         |
| `gcp-key`            | denylist           | `AIza[0-9A-Za-z_-]{35}`                               |
| `anthropic-key`      | denylist           | `sk-ant-` + 20+ chars                                 |
| `openai-key`         | denylist           | `sk-` + 20+ chars (after anthropic-key check)         |
| `oauth-code`         | denylist           | `access_token=`, `refresh_token=` + value             |
| `evm-private-key`    | denylist + context | contextual prefix + `0x[a-f0-9]{64}`                  |
| `solana-private-key` | denylist + context | contextual prefix + base58 86–90 chars                |
| `bearer`             | denylist           | `Bearer ` + 20–128 chars                              |
| `high-entropy`       | entropy            | ≥ 4.5 bits/char × ≥ 30 chars, not already redacted    |

Anthropic keys are matched before OpenAI keys in the denylist order because
both share the `sk-` prefix — earlier rules win when ranges overlap.

## Basic usage

```ts theme={null}
import { scan } from '@glideco/secrets-scan';

const result = scan(
  'key=AKIAIOSFODNN7EXAMPLE token=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
);

console.log(result.redactions);
// [
//   { kind: 'aws-access-key', match: 'AKIAIOSFODNN7EXAMPLE', start: 4, end: 24, reason: 'denylist' },
//   { kind: 'github-pat', match: 'ghp_xxxx...', start: 32, end: 69, reason: 'denylist' },
// ]

console.log(result.redacted);
// 'key=[REDACTED:aws-access-key] token=[REDACTED:github-pat]'
```

## Tuning the entropy pass

```ts theme={null}
import { scan } from '@glideco/secrets-scan';

// Turn off entropy for inputs that are naturally high-entropy
// (e.g. base64-encoded binary payloads, transaction hashes).
const result = scan(txHashHeavyLogLine, { disableEntropy: true });

// Tighten entropy threshold (default 4.5 bits/char, default min length 30).
const strict = scan(inputText, {
  entropyBitsPerChar: 4.8,
  entropyMinLength: 40,
});
```

## Custom redaction marker

```ts theme={null}
import { scan } from '@glideco/secrets-scan';

const result = scan(input, {
  marker: (kind) => `<REDACTED_${kind.toUpperCase()}>`,
});
// → 'prefix <REDACTED_AWS-ACCESS-KEY> suffix'
```

## Concurrent safety

Each `scan()` call clones every denylist regex so concurrent invocations
cannot race on the shared `lastIndex` state of a stateful global `RegExp`.
The Trust Console event ingestion path runs `scan()` in parallel under load —
sharing `lastIndex` would make the second concurrent scan's first-match index
non-deterministic, silently letting secrets through.

## Two-pass design

**Pass 1 — denylist.** 14 patterns, each with its own cloned `RegExp` instance.
Shape-only kinds (EVM private key, Solana private key, AWS secret key, Bearer
token) require a contextual word on the same line — "private", "secret", "key",
"seed", "Bearer" — so the scanner doesn't redact every transaction hash that
happens to be a 64-hex string.

**Pass 2 — entropy.** Shannon entropy (bits/character) on whitespace-tokenized
fragments not already covered by a denylist redaction range. Default threshold
is 4.5 bits/char over at least 30 characters — high enough to catch typical
API key formats, low enough to avoid false-positives on English prose.

```ts theme={null}
import { shannonEntropyBitsPerChar } from '@glideco/secrets-scan';

// Inspect the entropy score of a specific string.
const score = shannonEntropyBitsPerChar('sk-ant-api03-abcdef1234567890');
// → ~4.8 (above the 4.5 default threshold)
```

## CI gate

The Cathedral CI pipeline runs `node scripts/scan-secrets.mjs` on every PR.
The script calls `scan()` on all staged file contents and exits non-zero on
any redaction. Use `disableEntropy: true` on directories that legitimately
contain long base64 blobs (e.g. `test/fixtures/`).

## Reading list

* [Trust Console concepts](/docs/oss/concepts/trust-console) — how `ReasoningStepEvent` excerpts flow through the scanner before persistence.
* [Source on GitHub](https://github.com/darshanbathija/axtior-neobank/tree/main/packages/secrets-scan)
