first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
# @suaveplan/security-codes - Detect and sanitise sensitive security-code leakage in strings: API keys, JWTs, credit cards, GitHub tokens, Stripe keys, SSH keys, PEM blocks, AWS access keys, and more
## Summary
Detect and sanitise sensitive security-code leakage in strings. Handles API
keys, JWTs, credit cards, GitHub tokens, Stripe keys, SSH keys, PEM blocks,
AWS access keys, OpenAI keys, Google API keys, and high-entropy heuristics.
<!-- begin:auto-docs -->
## Documentation tree
- [code-detector](./src/core/code-detector/code-detector.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [code-detector](./src/core/code-detector/code-detector.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [code-sanitizer](./src/core/code-sanitizer/code-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [code-sanitizer](./src/core/code-sanitizer/code-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [errors](./src/errors/security-codes-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [security-codes-error](./src/errors/security-codes-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [aws](./src/patterns/aws.md) — Detects AWS Access Key IDs within arbitrary string input. AWS Access Key IDs are 20-character alphanumeric identifiers…
- [credit-card](./src/patterns/credit-card.md) — Detects credit card numbers embedded in arbitrary strings using a combination of regex matching and the Luhn checksum a…
- [entropy](./src/patterns/entropy.md) — Detects high-entropy strings that are likely secret tokens, API keys, or passwords using Shannon entropy analysis. This…
- [github](./src/patterns/github.md) — Detects GitHub Personal Access Tokens (PATs) — both classic tokens (`ghp_…`) and the newer fine-grained tokens (`github…
- [google](./src/patterns/google.md) — Detects Google API keys embedded in arbitrary strings. Google API keys begin with the well-known prefix `AIza` and are…
- [jwt](./src/patterns/jwt.md) — Detects JSON Web Tokens (JWTs) embedded in arbitrary strings. JWTs are bearer tokens with a well-defined three-part bas…
- [openai](./src/patterns/openai.md) — Detects OpenAI API keys in arbitrary strings. OpenAI API keys follow the format `sk-<48 chars>` for standard keys and `…
- [pem](./src/patterns/pem.md) — Detects PEM-encoded blocks in arbitrary strings. PEM (Privacy Enhanced Mail) is the standard encoding for TLS certifica…
- [ssh](./src/patterns/ssh.md) — Detects SSH private keys and public keys embedded in arbitrary strings. SSH private keys grant direct shell access to a…
- [stripe](./src/patterns/stripe.md) — Detects Stripe API keys — both secret keys (`sk_live_` / `sk_test_`) and publishable keys (`pk_live_` / `pk_test_`) — e…
- [types](./src/types/security-codes.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [security-codes](./src/types/security-codes.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [utils](./src/utils/luhn.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [luhn](./src/utils/luhn.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
<!-- end:auto-docs --> - [code-detector](./src/core/code-detector/code-detector.md) — <!-- begin:auto-docs --> _(no submodules)_- [code-sanitizer](./src/core/code-sanitizer/code-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_- [code-sanitizer](./src/core/code-sanitizer/code-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/security-codes-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [security-codes-error](./src/errors/security-codes-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [aws](./src/patterns/aws.md) — Detects AWS Access Key IDs within arbitrary string input. AWS Access Key IDs are 20-character alphanumeric identifiers…
- [credit-card](./src/patterns/credit-card.md) — Detects credit card numbers embedded in arbitrary strings using a combination of regex matching and the Luhn checksum a…
- [entropy](./src/patterns/entropy.md) — Detects high-entropy strings that are likely secret tokens, API keys, or passwords using Shannon entropy analysis. This…
- [github](./src/patterns/github.md) — Detects GitHub Personal Access Tokens (PATs) — both classic tokens (`ghp_…`) and the newer fine-grained tokens (`github…
- [google](./src/patterns/google.md) — Detects Google API keys embedded in arbitrary strings. Google API keys begin with the well-known prefix `AIza` and are…
- [jwt](./src/patterns/jwt.md) — Detects JSON Web Tokens (JWTs) embedded in arbitrary strings. JWTs are bearer tokens with a well-defined three-part bas…
- [openai](./src/patterns/openai.md) — Detects OpenAI API keys in arbitrary strings. OpenAI API keys follow the format `sk-<48 chars>` for standard keys and `…
- [pem](./src/patterns/pem.md) — Detects PEM-encoded blocks in arbitrary strings. PEM (Privacy Enhanced Mail) is the standard encoding for TLS certifica…
- [ssh](./src/patterns/ssh.md) — Detects SSH private keys and public keys embedded in arbitrary strings. SSH private keys grant direct shell access to a…
- [stripe](./src/patterns/stripe.md) — Detects Stripe API keys — both secret keys (`sk_live_` / `sk_test_`) and publishable keys (`pk_live_` / `pk_test_`) — e…
- [types](./src/types/security-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [security-codes](./src/types/security-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [utils](./src/utils/luhn.md) — <!-- begin:auto-docs --> _(no submodules)_- [luhn](./src/utils/luhn.md) — <!-- begin:auto-docs --> _(no submodules)_- [code-detector](./src/core/code-detector/code-detector.md) — <!-- begin:auto-docs --> _(no submodules)_- [code-sanitizer](./src/core/code-sanitizer/code-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_- [code-sanitizer](./src/core/code-sanitizer/code-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/security-codes-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [security-codes-error](./src/errors/security-codes-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [aws](./src/patterns/aws.md) — Detects AWS Access Key IDs within arbitrary string input. AWS Access Key IDs are 20-character alphanumeric identifiers…
- [credit-card](./src/patterns/credit-card.md) — Detects credit card numbers embedded in arbitrary strings using a combination of regex matching and the Luhn checksum a…
- [entropy](./src/patterns/entropy.md) — Detects high-entropy strings that are likely secret tokens, API keys, or passwords using Shannon entropy analysis. This…
- [github](./src/patterns/github.md) — Detects GitHub Personal Access Tokens (PATs) — both classic tokens (`ghp_…`) and the newer fine-grained tokens (`github…
- [google](./src/patterns/google.md) — Detects Google API keys embedded in arbitrary strings. Google API keys begin with the well-known prefix `AIza` and are…
- [jwt](./src/patterns/jwt.md) — Detects JSON Web Tokens (JWTs) embedded in arbitrary strings. JWTs are bearer tokens with a well-defined three-part bas…
- [openai](./src/patterns/openai.md) — Detects OpenAI API keys in arbitrary strings. OpenAI API keys follow the format `sk-<48 chars>` for standard keys and `…
- [pem](./src/patterns/pem.md) — Detects PEM-encoded blocks in arbitrary strings. PEM (Privacy Enhanced Mail) is the standard encoding for TLS certifica…
- [ssh](./src/patterns/ssh.md) — Detects SSH private keys and public keys embedded in arbitrary strings. SSH private keys grant direct shell access to a…
- [stripe](./src/patterns/stripe.md) — Detects Stripe API keys — both secret keys (`sk_live_` / `sk_test_`) and publishable keys (`pk_live_` / `pk_test_`) — e…
- [types](./src/types/security-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [security-codes](./src/types/security-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [utils](./src/utils/luhn.md) — <!-- begin:auto-docs --> _(no submodules)_- [luhn](./src/utils/luhn.md) — <!-- begin:auto-docs --> _(no submodules)_- [code-detector](./src/core/code-detector/code-detector.md) — <!-- begin:auto-docs --> _(no submodules)_- [code-sanitizer](./src/core/code-sanitizer/code-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_- [code-sanitizer](./src/core/code-sanitizer/code-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/security-codes-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [security-codes-error](./src/errors/security-codes-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [aws](./src/patterns/aws.md) — Detects AWS Access Key IDs within arbitrary string input. AWS Access Key IDs are 20-character alphanumeric identifiers…
- [credit-card](./src/patterns/credit-card.md) — Detects credit card numbers embedded in arbitrary strings using a combination of regex matching and the Luhn checksum a…
- [entropy](./src/patterns/entropy.md) — Detects high-entropy strings that are likely secret tokens, API keys, or passwords using Shannon entropy analysis. This…
- [github](./src/patterns/github.md) — Detects GitHub Personal Access Tokens (PATs) — both classic tokens (`ghp_…`) and the newer fine-grained tokens (`github…
- [google](./src/patterns/google.md) — Detects Google API keys embedded in arbitrary strings. Google API keys begin with the well-known prefix `AIza` and are…
- [jwt](./src/patterns/jwt.md) — Detects JSON Web Tokens (JWTs) embedded in arbitrary strings. JWTs are bearer tokens with a well-defined three-part bas…
- [openai](./src/patterns/openai.md) — Detects OpenAI API keys in arbitrary strings. OpenAI API keys follow the format `sk-<48 chars>` for standard keys and `…
- [pem](./src/patterns/pem.md) — Detects PEM-encoded blocks in arbitrary strings. PEM (Privacy Enhanced Mail) is the standard encoding for TLS certifica…
- [ssh](./src/patterns/ssh.md) — Detects SSH private keys and public keys embedded in arbitrary strings. SSH private keys grant direct shell access to a…
- [stripe](./src/patterns/stripe.md) — Detects Stripe API keys — both secret keys (`sk_live_` / `sk_test_`) and publishable keys (`pk_live_` / `pk_test_`) — e…
- [types](./src/types/security-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [security-codes](./src/types/security-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [utils](./src/utils/luhn.md) — <!-- begin:auto-docs --> _(no submodules)_- [luhn](./src/utils/luhn.md) — <!-- begin:auto-docs --> _(no submodules)_- [code-detector](./src/core/code-detector/code-detector.md) — <!-- begin:auto-docs --> _(no submodules)_- [code-sanitizer](./src/core/code-sanitizer/code-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_ - [code-sanitizer](./src/core/code-sanitizer/code-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/security-codes-error.md) — <!-- begin:auto-docs --> _(no submodules)_ - [security-codes-error](./src/errors/security-codes-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [aws](./src/patterns/aws.md) — Detects AWS Access Key IDs within arbitrary string input. AWS Access Key IDs are 20-character alphanumeric identifiers…
- [credit-card](./src/patterns/credit-card.md) — Detects credit card numbers embedded in arbitrary strings using a combination of regex matching and the Luhn checksum a…
- [entropy](./src/patterns/entropy.md) — Detects high-entropy strings that are likely secret tokens, API keys, or passwords using Shannon entropy analysis. This…
- [github](./src/patterns/github.md) — Detects GitHub Personal Access Tokens (PATs) — both classic tokens (`ghp_…`) and the newer fine-grained tokens (`github…
- [google](./src/patterns/google.md) — Detects Google API keys embedded in arbitrary strings. Google API keys begin with the well-known prefix `AIza` and are…
- [jwt](./src/patterns/jwt.md) — Detects JSON Web Tokens (JWTs) embedded in arbitrary strings. JWTs are bearer tokens with a well-defined three-part bas…
- [openai](./src/patterns/openai.md) — Detects OpenAI API keys in arbitrary strings. OpenAI API keys follow the format `sk-<48 chars>` for standard keys and `…
- [pem](./src/patterns/pem.md) — Detects PEM-encoded blocks in arbitrary strings. PEM (Privacy Enhanced Mail) is the standard encoding for TLS certifica…
- [ssh](./src/patterns/ssh.md) — Detects SSH private keys and public keys embedded in arbitrary strings. SSH private keys grant direct shell access to a…
- [stripe](./src/patterns/stripe.md) — Detects Stripe API keys — both secret keys (`sk_live_` / `sk_test_`) and publishable keys (`pk_live_` / `pk_test_`) — e…
- [types](./src/types/security-codes.md) — <!-- begin:auto-docs --> _(no submodules)_ - [security-codes](./src/types/security-codes.md) — <!-- begin:auto-docs --> _(no submodules)_- [utils](./src/utils/luhn.md) — <!-- begin:auto-docs --> _(no submodules)_ - [luhn](./src/utils/luhn.md) — <!-- begin:auto-docs --> _(no submodules)_
## Features
- **10 built-in pattern detectors** covering the most common secret types
- **Pluggable detector API** — register custom patterns at runtime
- **4 masking strategies** — full-redact, partial, hash, tokenize
- **Overlap deduplication** — higher-confidence matches win
- **OTel metrics** — `security_codes.detect.total` and `security_codes.detect.duration_ms`
- __Zero @suaveplan/_ runtime dependencies_* — leaf package, no circular risk
## Install
```bash
bun add @suaveplan/security-codes
```
## Quick Start
```ts
import { SecurityCodeDetector, SecurityCodeSanitizer } from '@suaveplan/security-codes';
const detector = new SecurityCodeDetector();
const matches = detector.detect('aws_key=AKIAIOSFODNN7EXAMPLE');
const sanitizer = new SecurityCodeSanitizer();
const { output } = sanitizer.sanitize('aws_key=AKIAIOSFODNN7EXAMPLE');
// output === 'aws_key=[REDACTED]'
```
## Module Docs
- [Types](src/types/security-codes.md)
- [Luhn utility](src/utils/luhn.md)
- [AWS pattern](src/patterns/aws.md)
- [JWT pattern](src/patterns/jwt.md)
- [Stripe pattern](src/patterns/stripe.md)
- [GitHub pattern](src/patterns/github.md)
- [Google pattern](src/patterns/google.md)
- [OpenAI pattern](src/patterns/openai.md)
- [Credit Card pattern](src/patterns/credit-card.md)
- [PEM pattern](src/patterns/pem.md)
- [SSH pattern](src/patterns/ssh.md)
- [Entropy pattern](src/patterns/entropy.md)
- [SecurityCodeDetector](src/core/code-detector/code-detector.md)
- [SecurityCodeSanitizer](src/core/code-sanitizer/code-sanitizer.md)

View File

@@ -0,0 +1,104 @@
# code-detector
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`SecurityCodeDetector` is the central orchestrator for secret detection in
`@suaveplan/security-codes`. It aggregates all built-in pattern modules,
allows registration of custom pattern functions at runtime, deduplicates
overlapping matches, and emits OpenTelemetry metrics for observability.
## Features
- All ten built-in detectors (AWS, JWT, Stripe, GitHub, Google, OpenAI,
credit card, PEM, SSH, entropy) are registered automatically at construction
time.
- `registerPattern()` accepts custom detectors for any `SecurityCodeType`,
enabling application-specific secret patterns to be added without forking
the library.
- Overlapping matches are resolved by retaining the match with the higher
confidence score; ties keep the earlier-starting match.
- `DetectorOptions.types` restricts which detectors run, reducing scan time
for pipelines that only care about specific secret types.
- `DetectorOptions.minConfidence` post-filters results below a caller-chosen
threshold.
- `DetectorOptions.maxMatches` caps the number of returned results.
- OTel counter `security_codes.detect.total` and histogram
`security_codes.detect.duration_ms` provide production observability.
## Usage: Basic Example
```ts
import { SecurityCodeDetector } from '@suaveplan/security-codes';
const detector = new SecurityCodeDetector();
const matches = detector.detect('aws_access_key=AKIAIOSFODNN7EXAMPLE');
// matches[0].type === 'aws-access-key'
// matches[0].confidence === 0.95
```
## Usage: Advanced Example
```ts
import { SecurityCodeDetector } from '@suaveplan/security-codes';
import type { SecurityCodeMatch } from '@suaveplan/security-codes';
const detector = new SecurityCodeDetector();
// Register a custom detector for internal API keys
detector.registerPattern('high-entropy', (input: string): SecurityCodeMatch[] => {
const re = /\bINT-[A-Z0-9]{32}\b/g;
const matches: SecurityCodeMatch[] = [];
let m: RegExpExecArray | null;
while ((m = re.exec(input)) !== null) {
matches.push({ type: 'high-entropy', value: m[0], start: m.index, end: m.index + m[0].length, confidence: 0.99 });
}
return matches;
});
// Detect only JWTs and credit cards with high confidence
const results = detector.detect(logLine, {
types: ['jwt', 'credit-card'],
minConfidence: 0.85,
maxMatches: 10,
});
```
## API Reference
### `new SecurityCodeDetector()`
Constructs a detector with all built-in patterns pre-registered.
### `registerPattern(type: SecurityCodeType, detector: (input: string) => SecurityCodeMatch[]): void`
Registers an additional detector function for the given type.
| Parameter | Type | Description |
|---|---|---|
| `type` | `SecurityCodeType` | The type this detector produces |
| `detector` | `DetectorFn` | Function that scans a string and returns matches |
Throws `SecurityCodesError(INVALID_PATTERN)` when `detector` is not a function.
### `detect(input: string, options?: DetectorOptions): SecurityCodeMatch[]`
Scans `input` and returns deduplicated, sorted matches.
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | String to scan |
| `options` | `DetectorOptions` | Optional filtering options |
Returns `SecurityCodeMatch[]` sorted by `start` index.
Throws `SecurityCodesError(DETECTION_FAILED)` when a pattern detector throws.
## Implementation Notes
The Stripe and GitHub pattern modules each detect multiple sub-types
internally (e.g. `stripe-secret` vs `stripe-publishable`). The detector
registers these modules once per function reference but post-filters by
requested type when `options.types` is provided. This avoids double-scanning
while still allowing consumers to restrict results to a single sub-type.

View File

@@ -0,0 +1,98 @@
# code-sanitizer
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`SecurityCodeSanitizer` takes arbitrary strings, detects all embedded
secrets via `SecurityCodeDetector`, and replaces each match with a
configurable mask. It supports four strategies — full redaction, partial
masking, FNV-1a hash substitution, and token placeholders — making it
suitable for log pipelines, audit trails, and reversible pseudonymisation.
## Features
- Delegates detection to a `SecurityCodeDetector` instance (injected or
default) so custom pattern registrations are automatically respected.
- Four masking strategies:
- `full-redact`: replaces the entire match with `[REDACTED]`
- `partial`: keeps the first and last four characters, replacing the
middle with `***`
- `hash`: replaces the match with an 8-character FNV-1a hex digest
of the original value
- `tokenize`: replaces the match with `TOKEN_<TYPE>_<N>` where N
increments per sanitize call
- `preserveLength` option pads or truncates the replacement to the same
byte length as the original, useful for fixed-width log formats.
- Replacements are applied right-to-left so that earlier indices remain
valid throughout the substitution pass.
- Throws `SecurityCodesError(SANITIZATION_FAILED)` on unexpected errors.
## Usage: Basic Example
```ts
import { SecurityCodeSanitizer } from '@suaveplan/security-codes';
const sanitizer = new SecurityCodeSanitizer();
const { output, matches } = sanitizer.sanitize(
'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ4In0.sig'
);
// output === 'Authorization: Bearer [REDACTED]'
// matches[0].type === 'jwt'
```
## Usage: Advanced Example
```ts
import { SecurityCodeSanitizer, SecurityCodeDetector } from '@suaveplan/security-codes';
const detector = new SecurityCodeDetector();
const sanitizer = new SecurityCodeSanitizer(detector);
// Tokenize for reversible pseudonymisation
const { output } = sanitizer.sanitize(
'key=AKIAIOSFODNN7EXAMPLE secret=sk_live_ABCDEFGHIJKLMNOPQRSTUVWX',
{ strategy: 'tokenize', tokenPrefix: 'SCRUBBED' }
);
// output contains 'SCRUBBED_AWS_ACCESS_KEY_0' and 'SCRUBBED_STRIPE_SECRET_1'
// Partial masking for readable logs
const partial = sanitizer.sanitize('card=4532015112830366', { strategy: 'partial' });
// partial.output === 'card=4532***0366'
```
## API Reference
### `new SecurityCodeSanitizer(detector?: SecurityCodeDetector)`
| Parameter | Type | Description |
|---|---|---|
| `detector` | `SecurityCodeDetector` | Optional custom detector; a default is created if omitted |
### `sanitize(input: string, options?: SanitizerOptions): SanitizeResult`
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to sanitize |
| `options` | `SanitizerOptions` | Optional masking configuration |
Returns `{ output: string; matches: SecurityCodeMatch[] }`.
Throws `SecurityCodesError(SANITIZATION_FAILED)` on errors.
**`SanitizerOptions`**
| Field | Default | Description |
|---|---|---|
| `strategy` | `'full-redact'` | Masking strategy |
| `preserveLength` | `false` | Pad/truncate replacement to original length |
| `tokenPrefix` | `'TOKEN'` | Prefix for `tokenize` strategy |
## Implementation Notes
The FNV-1a hash (`fnv1aHex`) is a fast, dependency-free 32-bit hash
producing an 8-character hex string. It is intentionally non-cryptographic
— use the `full-redact` strategy when one-way irreversibility is required.
Replacements are processed in descending start-index order to prevent index
drift: substituting a later match never affects the `start`/`end` of an
earlier one.

View File

@@ -0,0 +1,79 @@
# security-codes-error
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides the package-local error class and stable error-code registry for `@suaveplan/security-codes`. Because this package is a leaf with no `@suaveplan/error` dependency, it defines its own lightweight `SecurityCodesError` base class rather than extending `SuaveplanError`. The stable `SECURITY_CODES_CODES` constant object gives callers a programmatic handle for each failure mode without relying on fragile string matching against error messages.
## Features
- `SECURITY_CODES_CODES` constant object with three stable error codes: `DETECTION_FAILED`, `INVALID_PATTERN`, `SANITIZATION_FAILED`.
- `SecurityCodesCode` union type derived from the constant so callers can reference code values without importing a string literal.
- `SecurityCodesError` extends `Error` with an additional `readonly code: string` property for programmatic handling.
- `name` is set to `"SecurityCodesError"` so that `instanceof` checks and serialisation can distinguish this class from generic `Error` objects.
## Usage: Basic Example
```ts
import { SecurityCodesError, SECURITY_CODES_CODES } from '@suaveplan/security-codes/errors/security-codes-error';
try {
// some operation
} catch (err) {
if (err instanceof SecurityCodesError) {
console.error(err.code, err.message);
}
}
```
## Usage: Advanced Example
```ts
import { SecurityCodesError, SECURITY_CODES_CODES, SecurityCodesCode } from '@suaveplan/security-codes/errors/security-codes-error';
function handleCode(code: SecurityCodesCode): void {
switch (code) {
case SECURITY_CODES_CODES.DETECTION_FAILED:
// retry with simpler input
break;
case SECURITY_CODES_CODES.INVALID_PATTERN:
// remove the offending detector from the registry
break;
case SECURITY_CODES_CODES.SANITIZATION_FAILED:
// fall back to full-redact strategy
break;
}
}
```
## API Reference
### `SECURITY_CODES_CODES`
| Key | Value |
|---|---|
| `DETECTION_FAILED` | `"SECURITY_CODES.DETECTION_FAILED"` |
| `INVALID_PATTERN` | `"SECURITY_CODES.INVALID_PATTERN"` |
| `SANITIZATION_FAILED` | `"SECURITY_CODES.SANITIZATION_FAILED"` |
### `SecurityCodesCode`
`"SECURITY_CODES.DETECTION_FAILED" | "SECURITY_CODES.INVALID_PATTERN" | "SECURITY_CODES.SANITIZATION_FAILED"`
### `SecurityCodesError`
Extends `Error`.
| Member | Type | Description |
|---|---|---|
| `code` | `string` | Stable error code from `SECURITY_CODES_CODES` |
| `message` | `string` | Human-readable description |
| `name` | `"SecurityCodesError"` | Fixed for `instanceof` / serialisation |
**Constructor:** `new SecurityCodesError(message: string, code: string)`
## Implementation Notes
The package deliberately avoids depending on `@suaveplan/error` to keep `@suaveplan/security-codes` a true leaf package suitable for use in constrained environments. The error-code namespace prefix `SECURITY_CODES.` mirrors the convention used by `@suaveplan/error` consumers so that if the package ever graduates to a full `SuaveplanError` descendant the codes remain backward-compatible.

View File

@@ -0,0 +1,59 @@
# aws
## Purpose
Detects AWS Access Key IDs within arbitrary string input. AWS Access Key IDs
are 20-character alphanumeric identifiers that always begin with the prefix
`AKIA`. Detecting these in log lines, source code, or configuration strings
prevents inadvertent credential exposure.
## Features
- Regex-based detection using the canonical `AKIA[0-9A-Z]{16}` pattern.
- Word-boundary anchors (`\b`) reduce false positives from longer strings.
- Returns a confidence score of `0.95` — the fixed prefix makes false
positives very unlikely.
- Resets `lastIndex` before each scan so the exported function is safe for
repeated calls on different inputs.
## Usage: Basic Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/aws';
const matches = detect('Key: AKIAIOSFODNN7EXAMPLE');
// matches[0].type === 'aws-access-key'
// matches[0].confidence === 0.95
```
## Usage: Advanced Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/aws';
const log = 'aws_access_key_id=AKIAIOSFODNN7EXAMPLE aws_secret=abc123';
const matches = detect(log);
console.log(matches[0].start, matches[0].end);
// 20, 40 (covers just the key ID)
```
## API Reference
### `detect(input: string): SecurityCodeMatch[]`
Scans `input` for AWS Access Key IDs.
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to scan |
Returns an array of `SecurityCodeMatch` objects. Each match records the
matched value, its `[start, end)` position, and a `confidence` of `0.95`.
Returns an empty array when no keys are found.
## Implementation Notes
The pattern `/\bAKIA[0-9A-Z]{16}\b/g` is the standard detection heuristic
recommended by AWS and used by tools such as truffleHog and git-secrets.
Secret Access Keys (the companion credential) are not detected here because
they have no fixed prefix and are better covered by the entropy detector.

View File

@@ -0,0 +1,65 @@
# credit-card
## Purpose
Detects credit card numbers embedded in arbitrary strings using a combination
of regex matching and the Luhn checksum algorithm. Credit card numbers (PANs —
Primary Account Numbers) must never appear in logs, error messages, or
application state because they are governed by PCI-DSS compliance requirements.
## Features
- Matches 13–19 digit sequences, accepting space- or dash-separated formats
(e.g. `4532 0151 1283 0366` or `4532-0151-1283-0366`).
- Applies the Luhn algorithm to all candidates: only Luhn-valid numbers are
reported, significantly reducing false positives.
- Returns confidence `0.85` — the Luhn check is strong but not infallible
(random numbers can pass by coincidence).
- Word-boundary anchors prevent matching partial card numbers embedded in
longer digit strings.
## Usage: Basic Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/credit-card';
const matches = detect('card=4532015112830366');
// matches[0].type === 'credit-card'
// matches[0].confidence === 0.85
```
## Usage: Advanced Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/credit-card';
// Space-separated format
const matches = detect('Card: 4532 0151 1283 0366');
console.log(matches[0].value); // '4532 0151 1283 0366'
// Luhn-invalid number is not returned
const noMatch = detect('card=1234567890123456');
console.log(noMatch.length); // 0
```
## API Reference
### `detect(input: string): SecurityCodeMatch[]`
Scans `input` for credit card numbers.
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to scan |
Returns an array of `SecurityCodeMatch` with `type: 'credit-card'` and
`confidence: 0.85` for each Luhn-valid candidate. Non-Luhn-valid candidates
are silently discarded.
## Implementation Notes
The regex `\b(?:\d[ -]?){12,18}\d\b` matches the outer word boundary and then
requires 13–19 total digits with optional single-character separators between
each group. The Luhn validation in `../utils/luhn.ts` strips all non-digit
characters before computing the checksum, so space- and dash-separated formats
are validated correctly.

View File

@@ -0,0 +1,65 @@
# entropy
## Purpose
Detects high-entropy strings that are likely secret tokens, API keys, or
passwords using Shannon entropy analysis. This heuristic catches secrets that
have no fixed prefix and would be missed by pattern-based detectors.
While less precise than dedicated pattern detectors, it provides a useful
catch-all for novel or unrecognized credential formats.
## Features
- Scans for base64/hex-alphabet runs of at least 20 characters.
- Computes Shannon entropy (bits per character) for each candidate.
- Returns only candidates with entropy ≥ 4.5 bits per character, a threshold
calibrated to separate random tokens from natural-language substrings.
- Returns a deliberately conservative confidence of `0.6` to reflect the
heuristic nature of the detection.
## Usage: Basic Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/entropy';
// A random-looking base64 string
const matches = detect('secret=aB3dEfGhIjKlMnOpQrStUvWxYz012345678901');
// matches[0].type === 'high-entropy', confidence 0.6
```
## Usage: Advanced Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/entropy';
// Short or low-entropy strings are not flagged
const noMatch = detect('hello world this is plain text');
console.log(noMatch.length); // 0
// High-entropy token is flagged
const match = detect('TOKEN=xK8mP2nQ9rS5tV7wY1aB4cD6eF0gH3iJ');
console.log(match[0].confidence); // 0.6
```
## API Reference
### `detect(input: string): SecurityCodeMatch[]`
Scans `input` for high-entropy strings.
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to scan |
Returns an array of `SecurityCodeMatch` with `type: 'high-entropy'` and
`confidence: 0.6`. Returns an empty array when no high-entropy candidates
are found.
## Implementation Notes
Shannon entropy `H = -∑ p(x) log₂ p(x)` over a string's character frequency
distribution. Natural English text scores around 3.5–4.0 bits per character;
random tokens typically exceed 4.5. The threshold was chosen empirically to
minimise false positives on common identifiers and UUIDs while still catching
hex-encoded secrets and base64 tokens. The minimum length of 20 characters
filters out short words that happen to have high entropy due to their brevity.

View File

@@ -0,0 +1,70 @@
# github
## Purpose
Detects GitHub Personal Access Tokens (PATs) — both classic tokens
(`ghp_…`) and the newer fine-grained tokens (`github_pat_…`) — in
arbitrary strings. GitHub PATs grant repository and organization-level
access; leaking them in logs, CI output, or configuration files can lead
to repository takeover or data exfiltration.
## Features
- Separate regexes for classic PATs (`ghp_[A-Za-z0-9]{36}`) and
fine-grained PATs (`github_pat_[A-Za-z0-9_]{82}`).
- Fine-grained tokens receive a slightly higher confidence (`0.97`) due to
their longer, more distinctive prefix.
- Classic tokens score `0.95`.
- Word-boundary anchors prevent partial matches.
## Usage: Basic Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/github';
const matches = detect('GH_TOKEN=ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij');
// matches[0].type === 'github-pat'
```
## Usage: Advanced Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/github';
const text = 'classic=ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij fine=github_pat_' +
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDE' +
'FGHIJKLMN';
const matches = detect(text);
// matches[0].type === 'github-pat'
// matches[1].type === 'github-fine-grained-pat'
```
## API Reference
### `detect(input: string): SecurityCodeMatch[]`
Scans `input` for GitHub PATs (both variants).
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to scan |
Returns an array of `SecurityCodeMatch`. Classic PATs have
`confidence: 0.95`; fine-grained PATs have `confidence: 0.97`.
## Implementation Notes
GitHub's token format is documented in their REST API and security advisory
publications. The precise suffix lengths (36 for classic, 82 for
fine-grained) are derived from GitHub's own secret-scanning patterns. Both
regex objects reset `lastIndex` before each call to ensure correct behaviour
on repeated invocations.
Classic PATs are being phased out in favour of fine-grained tokens, which
scope permissions to specific repositories and organisations. Supporting both
formats future-proofs detection during the transition period. The higher
confidence score assigned to fine-grained tokens reflects their longer,
more distinctive prefix, which produces fewer coincidental matches in
arbitrary text. The `\b` word-boundary anchors on both sides prevent partial
matches where a longer base64-like string happens to start with the token
prefix.

View File

@@ -0,0 +1,65 @@
# google
## Purpose
Detects Google API keys embedded in arbitrary strings. Google API keys begin
with the well-known prefix `AIza` and are used to authenticate requests to
Google Cloud APIs, Maps, Firebase, and other Google services. Leaking these
keys in source code or logs can result in unexpected billing and unauthorized
access to Google services.
## Features
- Regex anchored with `\b` on both ends to avoid matching substrings of
longer tokens.
- Fixed 35-character suffix using URL-safe base64 characters (`[0-9A-Za-z-_]`).
- Returns confidence `0.9` — the fixed prefix is distinctive but not as unique
as AWS or Stripe prefixes.
## Usage: Basic Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/google';
const matches = detect('GOOGLE_API_KEY=AIzaSyDummyKey12345678901234567890123');
// matches[0].type === 'google-api-key'
// matches[0].confidence === 0.9
```
## Usage: Advanced Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/google';
const config = JSON.stringify({ apiKey: 'AIzaSyDummyKey12345678901234567890123' });
const matches = detect(config);
console.log(matches[0].start, matches[0].end);
```
## API Reference
### `detect(input: string): SecurityCodeMatch[]`
Scans `input` for Google API keys.
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to scan |
Returns an array of `SecurityCodeMatch` with `type: 'google-api-key'` and
`confidence: 0.9`.
## Implementation Notes
The `AIza` prefix is used by Google for API keys across many of their services.
The trailing character set (`[0-9A-Za-z-_]`) is the URL-safe base64 alphabet,
matching Google's actual key encoding. `lastIndex` is reset before each scan.
The confidence value of `0.9` is intentionally conservative relative to
Stripe or GitHub patterns, because the `AIza` prefix is shorter and the
character set is broad enough that coincidental matches in large documents are
plausible. Callers that process Google-specific configuration files may wish to
raise `minConfidence` to `0.95` and accept the small risk of missing an
obfuscated key, while callers performing broad log sweeps should leave it at
the default to maximise recall. The 35-character suffix length matches the
actual key length as observed in Google Cloud Console-issued credentials.

View File

@@ -0,0 +1,64 @@
# jwt
## Purpose
Detects JSON Web Tokens (JWTs) embedded in arbitrary strings. JWTs are
bearer tokens with a well-defined three-part base64url structure
(`header.payload.signature`). Leaking JWTs in logs, error messages, or
configuration files grants an attacker the same privileges as the original
holder until the token expires.
## Features
- Regex matches the canonical `eyJ...` header prefix followed by two more
base64url segments separated by dots.
- Secondary validation: decodes and JSON-parses the header segment to
confirm the match is a real JWT (not a coincidental dot-separated string).
- Uses `atob` for base64url decoding — available in all modern runtimes
including Node.js ≥16 and all browsers.
- Returns confidence `0.9` — the structural validation makes false positives
unlikely, while `1.0` is reserved for signatures that are cryptographically
verified.
## Usage: Basic Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/jwt';
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
const matches = detect(`Authorization: Bearer ${token}`);
// matches[0].type === 'jwt'
```
## Usage: Advanced Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/jwt';
// Multiple tokens in a single string
const text = 'token1=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ4In0.abc token2=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ4In0.def';
const matches = detect(text);
console.log(matches.length); // 2
```
## API Reference
### `detect(input: string): SecurityCodeMatch[]`
Scans `input` for JWTs.
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to scan |
Returns an array of `SecurityCodeMatch` with `type: 'jwt'` and
`confidence: 0.9`. Returns an empty array when no valid JWTs are found.
Matches that pass the regex but fail the header JSON-parse are silently
dropped.
## Implementation Notes
The regex anchors on `\b` to avoid matching base64 blobs embedded inside
longer tokens. The header validation uses `atob` rather than a Node-only
`Buffer` to keep the module runtime-agnostic. The signature segment is not
cryptographically verified — that is outside the scope of detection.

View File

@@ -0,0 +1,65 @@
# openai
## Purpose
Detects OpenAI API keys in arbitrary strings. OpenAI API keys follow the
format `sk-<48 chars>` for standard keys and `sk-proj-<48+ chars>` for
project-scoped keys. Exposing these credentials allows unauthorized access
to OpenAI's models, incurring cost and potential data leakage.
## Features
- Handles both standard (`sk-`) and project-scoped (`sk-proj-`) key formats.
- Project keys are matched first; the shorter `sk-` pattern skips positions
already claimed by a project key to avoid double-reporting.
- Project keys receive a slightly higher confidence (`0.95`) than standard
keys (`0.92`) due to the more distinctive prefix.
- Word-boundary anchors on both ends.
## Usage: Basic Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/openai';
const matches = detect('OPENAI_API_KEY=sk-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv');
// matches[0].type === 'openai-key'
// matches[0].confidence === 0.92
```
## Usage: Advanced Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/openai';
const text = 'key=sk-proj-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const matches = detect(text);
// matches[0].type === 'openai-key'
// matches[0].confidence === 0.95
```
## API Reference
### `detect(input: string): SecurityCodeMatch[]`
Scans `input` for OpenAI API keys.
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to scan |
Returns an array of `SecurityCodeMatch` with `type: 'openai-key'`.
Standard keys have `confidence: 0.92`; project keys have `confidence: 0.95`.
## Implementation Notes
The project-key regex (`sk-proj-`) is tested first and winning positions are
recorded in a `Set` so the standard-key regex does not produce duplicate
matches for the same token. Both regexes reset `lastIndex` before scanning.
OpenAI introduced project-scoped keys to allow tighter permission boundaries
per API project. Detecting them separately lets security tooling distinguish
between a leaked organisation-wide key (higher blast radius) and a
project-scoped key (limited blast radius). The 48-character suffix length for
standard keys and the extended suffix for project keys match OpenAI's published
key format; the exact length boundary is enforced via `{48,}` to tolerate any
future increases without requiring a regex update.

View File

@@ -0,0 +1,67 @@
# pem
## Purpose
Detects PEM-encoded blocks in arbitrary strings. PEM (Privacy Enhanced Mail)
is the standard encoding for TLS certificates, RSA/EC private keys, certificate
signing requests, and public keys. Leaking private-key PEM blocks in logs or
error messages compromises the entire cryptographic identity of the affected
service.
## Features
- Regex uses a non-greedy `[\s\S]*?` between the `-----BEGIN … -----` and
`-----END … -----` markers to match the full block including newlines.
- Matches any PEM type: `RSA PRIVATE KEY`, `EC PRIVATE KEY`,
`CERTIFICATE`, `CERTIFICATE REQUEST`, `PUBLIC KEY`, etc.
- Returns confidence `0.97` — the fixed delimiter format makes false positives
extremely unlikely.
## Usage: Basic Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/pem';
const pem = '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0Z3V...\n-----END RSA PRIVATE KEY-----';
const matches = detect(pem);
// matches[0].type === 'pem-block'
```
## Usage: Advanced Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/pem';
// Embedded in a config file
const config = `tls_cert: |\n -----BEGIN CERTIFICATE-----\n MIIBxTCC...\n -----END CERTIFICATE-----`;
const matches = detect(config);
console.log(matches[0].confidence); // 0.97
```
## API Reference
### `detect(input: string): SecurityCodeMatch[]`
Scans `input` for PEM blocks.
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to scan (may contain newlines) |
Returns an array of `SecurityCodeMatch` with `type: 'pem-block'` and
`confidence: 0.97`. The `value` includes the full block including header,
base64 content, and footer.
## Implementation Notes
The regex uses the `g` flag with `lastIndex` reset before each call. The
`[\s\S]*?` pattern enables cross-line matching without the `s` (dotAll) flag,
ensuring compatibility with environments where dotAll behaviour may differ.
The non-greedy quantifier is critical here: a greedy `[\s\S]*` would consume
everything between the first `-----BEGIN` and the last `-----END` in a
multi-certificate chain, collapsing multiple blocks into a single oversized
match. The non-greedy form correctly yields one match per PEM block. The
confidence of `0.97` is among the highest in the package because the
`-----BEGIN … -----` / `-----END … -----` framing is extremely unlikely to
appear in non-PEM text, making false positives rare even in noisy inputs.

View File

@@ -0,0 +1,66 @@
# ssh
## Purpose
Detects SSH private keys and public keys embedded in arbitrary strings.
SSH private keys grant direct shell access to any server that has the
corresponding public key in its `authorized_keys`. Public keys in authorized
format can reveal the owner's cryptographic identity and are also worth
flagging in security audits.
## Features
- Detects OpenSSH private key blocks (`-----BEGIN OPENSSH PRIVATE KEY-----`),
RSA public keys (`ssh-rsa AAAA…`), ECDSA keys (`ecdsa-sha2-nistp256 AAAA…`),
and Ed25519 keys (`ssh-ed25519 AAAA…`).
- Private key blocks receive a higher confidence score (`0.98`) than public
keys (`0.92`).
- All four regex patterns reset `lastIndex` before each scan.
## Usage: Basic Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/ssh';
const key = '-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXkAAAA=\n-----END OPENSSH PRIVATE KEY-----';
const matches = detect(key);
// matches[0].type === 'ssh-key', confidence 0.98
```
## Usage: Advanced Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/ssh';
const config = 'authorized_key=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ user@host';
const matches = detect(config);
// matches[0].type === 'ssh-key', confidence 0.92
```
## API Reference
### `detect(input: string): SecurityCodeMatch[]`
Scans `input` for SSH keys.
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to scan (may contain newlines) |
Returns `SecurityCodeMatch[]` with `type: 'ssh-key'`. Private key blocks
have `confidence: 0.98`; public key formats have `confidence: 0.92`.
## Implementation Notes
OpenSSH private keys use PEM-like delimiters but with a specific header string
that distinguishes them from generic PEM blocks detected by the `pem` module.
Both modules may return matches for the same region — the detector deduplication
layer resolves overlaps by preferring higher-confidence matches.
Public key formats (`ssh-rsa`, `ecdsa-sha2-nistp256`, `ssh-ed25519`) are
matched with a permissive `\S+` suffix to handle the variable-length base64
blob and the optional trailing comment (e.g. `user@host`). The lower confidence
score of `0.92` for public keys reflects the fact that a standalone `ssh-rsa
AAAA…` string in a configuration file is common and not necessarily a secret
leak, whereas an OpenSSH private key block is always sensitive and receives
`0.98`.

View File

@@ -0,0 +1,68 @@
# stripe
## Purpose
Detects Stripe API keys — both secret keys (`sk_live_` / `sk_test_`) and
publishable keys (`pk_live_` / `pk_test_`) — embedded in arbitrary strings.
While publishable keys are technically public, detecting them prevents
environment confusion. Secret keys provide full API access and must never
appear in logs or source code.
## Features
- Separate regexes for secret and publishable prefixes to distinguish types.
- Live secret keys receive a higher confidence score (`0.99`) than test
variants (`0.9`) because live credentials are more sensitive.
- Publishable keys score `0.9` regardless of environment.
- Minimum 24-character suffix requirement filters out truncated or partial
matches.
## Usage: Basic Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/stripe';
const matches = detect('STRIPE_SECRET=sk_live_ABCDEFGHIJKLMNOPQRSTUVWX');
// matches[0].type === 'stripe-secret'
// matches[0].confidence === 0.99
```
## Usage: Advanced Example
```ts
import { detect } from '@suaveplan/security-codes/patterns/stripe';
const env = 'sk_test_TESTKEY1234567890123456 pk_live_PUBKEY1234567890123456';
const matches = detect(env);
// matches[0].type === 'stripe-secret', confidence 0.9 (test key)
// matches[1].type === 'stripe-publishable', confidence 0.9
```
## API Reference
### `detect(input: string): SecurityCodeMatch[]`
Scans `input` for Stripe secret and publishable keys.
| Parameter | Type | Description |
|---|---|---|
| `input` | `string` | The string to scan |
Returns an array of `SecurityCodeMatch` objects. Live secret keys have
`confidence: 0.99`, all other variants have `confidence: 0.9`.
## Implementation Notes
Stripe's key format is documented in their API reference. The `\b` word
boundary on both ends prevents matching a key that is part of a longer
alphanumeric string. Both regex objects have `lastIndex` reset before each
scan to handle repeated calls safely.
Distinguishing live from test keys matters because test keys can be committed
to public repositories intentionally (for CI integration tests), while live
keys must never appear outside of secrets management infrastructure. The
confidence delta between `sk_live_` (`0.99`) and `sk_test_` (`0.9`) allows
callers to apply a stricter response policy to live keys — for example,
immediately revoking them via Stripe's API — while treating test-key
detections as lower-priority warnings. Publishable keys are tracked at `0.9`
to surface environment-confusion issues during code review.

View File

@@ -0,0 +1,127 @@
# security-codes.types
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Defines the shared TypeScript types used throughout `@suaveplan/security-codes`.
These types form the public contract between the pattern library, the detector,
the sanitizer, and any consumer. Having them in a dedicated file ensures that
feature modules can import only the type contract without pulling in any
runtime code.
## Features
- `SecurityCodeType` — a discriminated union of all secret/token pattern names
the library supports (AWS keys, JWTs, credit cards, GitHub tokens, Stripe
keys, Google API keys, OpenAI keys, PEM blocks, SSH keys, and high-entropy
heuristics).
- `MaskingStrategy` — union of the four masking modes understood by the
sanitizer: `full-redact`, `partial`, `hash`, and `tokenize`.
- `SecurityCodeMatch` — the detection result record: type, original value,
start/end indices, and a confidence score `[0, 1]`.
- `DetectorOptions` — filtering knobs for the detector: restrict by type,
set a minimum confidence threshold, and cap the number of results.
- `SanitizerOptions` — sanitizer configuration: choose a masking strategy,
control length-preservation, and override the token prefix.
- `SanitizeResult` — the sanitizer return value bundling the masked output
string together with all replacement records.
## Usage: Basic Example
```ts
import type { SecurityCodeMatch, MaskingStrategy } from '@suaveplan/security-codes';
const match: SecurityCodeMatch = {
type: 'aws-access-key',
value: 'AKIAIOSFODNN7EXAMPLE',
start: 0,
end: 20,
confidence: 0.95,
};
const strategy: MaskingStrategy = 'partial';
```
## Usage: Advanced Example
```ts
import type {
DetectorOptions,
SanitizerOptions,
SanitizeResult,
} from '@suaveplan/security-codes';
const detectOpts: DetectorOptions = {
types: ['jwt', 'stripe-secret', 'credit-card'],
minConfidence: 0.8,
maxMatches: 20,
};
const sanitizeOpts: SanitizerOptions = {
strategy: 'tokenize',
tokenPrefix: 'MASKED',
};
// sanitize() returns SanitizeResult
const result: SanitizeResult = {
output: 'Bearer MASKED_JWT_0',
matches: [],
};
```
## API Reference
### `SecurityCodeType`
`type SecurityCodeType = 'aws-access-key' | 'jwt' | 'credit-card' | 'github-pat' | 'github-fine-grained-pat' | 'stripe-secret' | 'stripe-publishable' | 'google-api-key' | 'openai-key' | 'pem-block' | 'ssh-key' | 'high-entropy'`
String union of all supported detection pattern identifiers.
### `MaskingStrategy`
`type MaskingStrategy = 'full-redact' | 'partial' | 'hash' | 'tokenize'`
Enumeration of masking strategies accepted by the sanitizer.
### `SecurityCodeMatch`
| Field | Type | Description |
|---|---|---|
| `type` | `SecurityCodeType` | Classified pattern type |
| `value` | `string` | Exact matched substring |
| `start` | `number` | Inclusive start index |
| `end` | `number` | Exclusive end index |
| `confidence` | `number` | Score in `[0, 1]` |
### `DetectorOptions`
| Field | Type | Default | Description |
|---|---|---|---|
| `types` | `SecurityCodeType[]` | all | Restrict to these types |
| `minConfidence` | `number` | `0` | Suppress below this score |
| `maxMatches` | `number` | unlimited | Return at most N results |
### `SanitizerOptions`
| Field | Type | Default | Description |
|---|---|---|---|
| `strategy` | `MaskingStrategy` | `'full-redact'` | Masking mode |
| `preserveLength` | `boolean` | `false` | Pad/truncate to original length |
| `tokenPrefix` | `string` | `'TOKEN'` | Prefix for `tokenize` strategy |
### `SanitizeResult`
| Field | Type | Description |
|---|---|---|
| `output` | `string` | Sanitized output string |
| `matches` | `SecurityCodeMatch[]` | Detected and replaced secrets |
## Implementation Notes
All types in this file are pure TypeScript interfaces and type aliases — no
runtime code is emitted. This means importing from `security-codes.types` has
zero bundle-size cost. The `confidence` field follows a convention used
throughout the pattern library: deterministic patterns with well-known prefixes
score ≥ 0.90, while heuristic detectors (Shannon entropy) score around 0.60.

View File

@@ -0,0 +1,97 @@
# security-codes.types
## Purpose
Defines all shared TypeScript interfaces and union types for the `@suaveplan/security-codes` package. These types are the single source of truth consumed by every detector module, the `SecurityCodeSanitizer`, and any external caller that processes detection results. Keeping them in one file makes it straightforward to add new detector variants or masking strategies without touching multiple implementation files.
## Features
- `SecurityCodeType` union covering all twelve supported secret categories (AWS, JWT, credit-card, GitHub classic/fine-grained, Stripe secret/publishable, Google, OpenAI, PEM, SSH, high-entropy).
- `MaskingStrategy` union with four named strategies: `full-redact`, `partial`, `hash`, `tokenize`.
- `SecurityCodeMatch` result shape with position metadata (`start`, `end`) and a calibrated `confidence` score.
- `DetectorOptions` for filtering which detectors run, setting a minimum confidence threshold, and capping result count.
- `SanitizerOptions` for selecting a masking strategy, preserving byte length, and customising the tokenize prefix.
- `SanitizeResult` wrapping the sanitised output string together with all matches that were replaced.
## Usage: Basic Example
```ts
import type { SecurityCodeMatch, MaskingStrategy } from '@suaveplan/security-codes';
function printMatch(m: SecurityCodeMatch): void {
console.log(`${m.type} at [${m.start},${m.end}] confidence=${m.confidence}`);
}
```
## Usage: Advanced Example
```ts
import type {
DetectorOptions,
SanitizerOptions,
SanitizeResult,
} from '@suaveplan/security-codes';
const detectOpts: DetectorOptions = {
types: ['aws-access-key', 'github-pat'],
minConfidence: 0.9,
maxMatches: 10,
};
const sanitizeOpts: SanitizerOptions = {
strategy: 'tokenize',
tokenPrefix: 'SECRET',
};
function logResult(result: SanitizeResult): void {
console.log('Sanitized:', result.output);
console.log('Replaced:', result.matches.length, 'secrets');
}
```
## API Reference
### `SecurityCodeType`
String literal union for all classifiable secret types. Values: `"aws-access-key"`, `"jwt"`, `"credit-card"`, `"github-pat"`, `"github-fine-grained-pat"`, `"stripe-secret"`, `"stripe-publishable"`, `"google-api-key"`, `"openai-key"`, `"pem-block"`, `"ssh-key"`, `"high-entropy"`.
### `MaskingStrategy`
`"full-redact" | "partial" | "hash" | "tokenize"`
### `SecurityCodeMatch`
| Field | Type | Description |
|---|---|---|
| `type` | `SecurityCodeType` | Classified secret category |
| `value` | `string` | Exact matched substring |
| `start` | `number` | Inclusive start offset in input |
| `end` | `number` | Exclusive end offset in input |
| `confidence` | `number` | Score in `[0, 1]` |
### `DetectorOptions`
| Field | Type | Default | Description |
|---|---|---|---|
| `types` | `SecurityCodeType[]?` | all | Restrict to these detectors |
| `minConfidence` | `number?` | `0` | Suppress low-confidence matches |
| `maxMatches` | `number?` | unlimited | Cap result count |
### `SanitizerOptions`
| Field | Type | Default | Description |
|---|---|---|---|
| `strategy` | `MaskingStrategy?` | `"full-redact"` | Replacement strategy |
| `preserveLength` | `boolean?` | `false` | Pad/truncate replacement to original length |
| `tokenPrefix` | `string?` | `"TOKEN"` | Prefix for tokenize strategy |
### `SanitizeResult`
| Field | Type | Description |
|---|---|---|
| `output` | `string` | Input with secrets replaced |
| `matches` | `SecurityCodeMatch[]` | All detected and replaced secrets |
## Implementation Notes
Confidence scores are calibrated per-detector based on the specificity of the matched pattern. Known-prefix formats (Stripe `sk_live_`, GitHub `ghp_`) score in the range `0.90–0.99`. Heuristic detection (high-entropy strings) scores around `0.60` to reflect the higher false-positive rate. Callers should treat the `minConfidence` threshold as the primary tuning knob for precision/recall trade-offs.

View File

@@ -0,0 +1,69 @@
# luhn
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Implements the Luhn algorithm for credit-card number validation. The Luhn
check is a widely-used checksum formula that can quickly determine whether a
sequence of digits is plausibly a valid credit-card number, and is used here
to reduce false-positive detections in the `credit-card` pattern module.
## Features
- Strips non-digit characters before validating, accepting space- or
dash-separated card number formats.
- O(n) implementation with no allocations beyond the cleaned digit string.
- Returns `false` immediately for inputs shorter than 2 digits.
## Usage: Basic Example
```ts
import { luhnCheck } from '@suaveplan/security-codes/utils/luhn';
luhnCheck('4532015112830366'); // true — valid Visa test number
luhnCheck('1234567890123456'); // false — fails Luhn
```
## Usage: Advanced Example
```ts
import { luhnCheck } from '@suaveplan/security-codes/utils/luhn';
// Space-separated card numbers are handled transparently
luhnCheck('4532 0151 1283 0366'); // true
// Short or empty strings are safely rejected
luhnCheck(''); // false
luhnCheck('4'); // false
```
## API Reference
### `luhnCheck(digits: string): boolean`
| Parameter | Type | Description |
|---|---|---|
| `digits` | `string` | Raw digit string; non-digit chars are stripped before validation |
Returns `true` when `digits` passes the Luhn checksum and contains at least
two digit characters, `false` otherwise. Does not throw.
## Implementation Notes
The algorithm doubles every second digit from the right (starting at index
`length - 2`). When doubling produces a value greater than 9, 9 is subtracted.
All digits are summed; a total divisible by 10 indicates a valid number.
This implementation intentionally avoids any dependency on built-in crypto or
external libraries so that it can be used in both Node.js and browser
environments without a polyfill.
The Luhn check is used by the `credit-card` pattern module as a secondary gate
after the initial regex match. A 16-digit string matching the card-number
pattern but failing the Luhn check is discarded rather than reported, which
significantly reduces false positives in documents containing order IDs,
tracking numbers, or other long numeric strings. Note that Luhn validation
confirms mathematical plausibility only — it does not verify that the card
number has ever been issued or is currently active.