first commit
This commit is contained in:
116
docs/security-stack/security/security-codes/README.md
Normal file
116
docs/security-stack/security/security-codes/README.md
Normal 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)
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user