glin-profanity
Version:
Glin-Profanity is a lightweight and efficient npm package designed to detect and filter profane language in text inputs across multiple languages. Whether you’re building a chat application, a comment section, or any platform where user-generated content
409 lines (397 loc) • 16 kB
text/typescript
/**
* Base types and interfaces for the glin-profanity scanner system.
* Inspired by ProtectAI's llm-guard and Meta's LlamaFirewall scanner interface.
*
* @module scanners/base
*/
/** The final decision a scanner makes about an input. */
type ScanDecision = 'ALLOW' | 'BLOCK' | 'HITL';
/** A match found within the input string. */
interface ScanMatch {
/** The pattern identifier or description that matched. */
pattern: string;
/** Zero-based start index of the match within the input. */
startIndex: number;
/** Zero-based end index (exclusive) of the match within the input. */
endIndex: number;
/** The category this match belongs to. */
category: string;
}
/** The result returned by a scanner after processing an input. */
interface ScanResult {
/** The sanitized version of the input (may equal input if no changes). */
sanitized: string;
/** Whether the input is considered safe (true = safe, false = flagged). */
valid: boolean;
/** Risk score from 0 (safe) to 1 (maximum risk). */
score: number;
/** The decision the scanner reached. */
decision: ScanDecision;
/** Human-readable reasons why the input was flagged. */
reasons: string[];
/** Detailed match information, if available. */
matches?: ScanMatch[];
/** The name of the scanner that produced this result, used for telemetry. */
scanner: string;
}
/** A content scanner that evaluates an input string. */
interface Scanner {
/** Unique name identifying this scanner. */
readonly name: string;
/** Evaluate the input and return a scan result. */
scan(input: string, ctx?: ScanContext): Promise<ScanResult> | ScanResult;
}
/** Optional context provided to a scanner to influence its behaviour. */
interface ScanContext {
/** The role of the message author in the conversation. */
role?: 'USER' | 'ASSISTANT' | 'TOOL' | 'SYSTEM' | 'MEMORY';
/** How aggressively the scanner should flag content. */
strictness?: 'lenient' | 'moderate' | 'strict';
}
/**
* Build an ALLOW result for a scanner that found nothing to flag.
*
* @param scanner - Name of the scanner producing the result.
* @param input - The original input string.
*/
declare function allowResult(scanner: string, input: string): ScanResult;
/**
* Build a BLOCK, HITL, or ALLOW result based on score thresholds.
* Returns BLOCK when score ≥ blockAt, HITL when score ≥ hitlAt, and
* ALLOW when score < hitlAt (even though matches were found).
*
* @param scanner - Name of the scanner producing the result.
* @param input - The original input string.
* @param score - Computed risk score (0..1).
* @param reasons - List of human-readable flag reasons.
* @param matches - Optional per-pattern match details.
* @param blockAt - Threshold at or above which the decision is BLOCK (default 0.8).
* @param hitlAt - Threshold at or above which the decision is HITL (default 0.5).
*/
declare function blockResult(scanner: string, input: string, score: number, reasons: string[], matches?: ScanMatch[], blockAt?: number, hitlAt?: number): ScanResult;
/**
* Prompt injection pattern database for the glin-profanity scanner.
*
* Sources: PromptGuard categories, OWASP LLM Top-10, public jailbreak corpora
* (jailbreakbench.github.io, promptinjections.com research, llm-guard open patterns).
* All patterns are original regex constructions under MIT license — no GPL sources copied.
*
* @module scanners/patterns/injection-patterns
*/
/** Categories of prompt injection attack patterns. */
type InjectionCategory = 'instruction_override' | 'jailbreak_persona' | 'system_prompt_leak' | 'delimiter_injection' | 'encoding_bypass' | 'tool_misuse';
/** Severity level of a pattern match. */
type PatternSeverity = 'low' | 'medium' | 'high' | 'critical';
/** A single injection detection pattern entry. */
interface InjectionPattern {
/** Stable identifier (e.g. PI-001). */
id: string;
/** The regex to match against input text. */
pattern: RegExp;
/** Which attack category this pattern belongs to. */
category: InjectionCategory;
/** How dangerous a match on this pattern is considered. */
severity: PatternSeverity;
/** Short human-readable description of the pattern. */
description: string;
}
declare const INJECTION_PATTERNS: InjectionPattern[];
/**
* Rule-based prompt injection scanner (Phase A).
*
* Uses a pattern database to detect common prompt injection techniques.
* Phase B (ONNX model) is planned and will be composed with this scanner.
*
* @module scanners/prompt-injection
*/
/** Configuration options for the PromptInjectionScanner. */
interface PromptInjectionOptions {
/**
* How aggressively to score matches.
* - `lenient`: uses a higher normalizer that divides the raw severity sum more
* aggressively, producing lower scores and requiring more evidence to block.
* - `moderate` (default): balanced normalizer.
* - `strict`: lowest normalizer, so even a small number of pattern matches can
* push the score to BLOCK.
*/
strictness?: 'lenient' | 'moderate' | 'strict';
/** Additional custom patterns to check alongside the built-in set. */
customPatterns?: InjectionPattern[];
/** Score threshold at or above which the decision is BLOCK (default: 0.8). */
blockAt?: number;
/** Score threshold at or above which the decision is HITL (default: 0.5). */
hitlAt?: number;
}
/** Rule-based scanner that detects prompt injection patterns in text. */
declare class PromptInjectionScanner implements Scanner {
/** @inheritdoc */
readonly name = "prompt-injection";
private readonly compiledPatterns;
private readonly strictness;
private readonly blockAt;
private readonly hitlAt;
constructor(options?: PromptInjectionOptions);
/** @inheritdoc */
scan(input: string, ctx?: ScanContext): ScanResult;
}
/** Convenience singleton using default (moderate) settings. */
declare const defaultPromptInjectionScanner: PromptInjectionScanner;
/**
* Scan a single string for prompt injection signals using configurable options.
*
* @param input - The text to scan.
* @param options - Optional scanner configuration.
* @returns A ScanResult with decision, score, and match details.
*/
declare function checkPromptInjection(input: string, options?: PromptInjectionOptions): ScanResult;
/**
* Secret detection patterns for the SecretsScanner.
*
* Pattern attribution:
* - ProtectAI/llm-guard (MIT) — https://github.com/protectai/llm-guard
* - Yelp/detect-secrets (Apache-2.0) — https://github.com/Yelp/detect-secrets
*
* Patterns have been reimplemented in TypeScript — no source was copied verbatim.
*
* @module scanners/patterns/secret-patterns
*/
/** Severity level for a detected secret. */
type SecretSeverity = 'low' | 'medium' | 'high' | 'critical';
/** A single secret detection rule. */
interface SecretPattern {
/** Unique identifier, e.g. "SEC-AWS-001". */
id: string;
/** Human-readable name shown in scan results. */
name: string;
/** Compiled regular expression for detection. */
pattern: RegExp;
/** Severity of exposure if this secret is leaked. */
severity: SecretSeverity;
/**
* Short family label used in `ScanMatch.category` and redaction placeholders,
* e.g. "aws", "stripe", "github". When omitted, derived from the `id` by
* lowercasing the middle segment (e.g. "SEC-STRIPE-001" → "stripe").
*/
family?: string;
/**
* When true, also require Shannon entropy > 4.0 on the matched token
* before flagging. Reduces false-positives on placeholder/example strings.
*/
entropyCheck?: boolean;
}
/**
* All built-in secret detection patterns.
* Total: ≥100 patterns covering major cloud/SaaS providers.
*/
declare const SECRET_PATTERNS: SecretPattern[];
/**
* Vault — stores redacted originals and restores them after pipeline processing.
*
* Design ported from ProtectAI/llm-guard (MIT) — rewritten in TypeScript.
* Supports four restore strategies: exact, caseInsensitive, fuzzy, combined.
*
* @module scanners/vault
*/
/** A single entry stored in the vault. */
interface VaultEntry {
/** Placeholder token used in sanitized text, e.g. "[REDACTED_EMAIL_1]". */
placeholder: string;
/** The original sensitive value. */
original: string;
/** Type string, e.g. "EMAIL", used in placeholder construction. */
type: string;
}
/** Strategy for matching placeholders back to originals when restoring. */
type RestoreStrategy = 'exact' | 'caseInsensitive' | 'fuzzy' | 'combined';
/**
* Vault stores placeholder→original mappings produced during redaction and
* replaces placeholders with originals when text leaves the pipeline.
*
* Inspired by ProtectAI/llm-guard vault.py (MIT).
*/
declare class Vault {
private entries;
/** Counters per type so each new entry gets a unique sequential number. */
private counters;
/**
* Store an original value and return a unique placeholder string.
*
* @param type - PII/secret type label (will be upper-cased), e.g. "email".
* @param original - The raw sensitive value to redact.
* @returns The placeholder token, e.g. "[REDACTED_EMAIL_1]".
*/
store(type: string, original: string): string;
/**
* Replace all placeholders in `text` with their original values.
*
* @param text - The sanitized text containing placeholder tokens.
* @param strategy - How to match placeholders (default: `'exact'`).
* @returns The restored text with originals substituted back.
*/
restore(text: string, strategy?: RestoreStrategy): string;
/** Remove all stored entries and reset counters. */
clear(): void;
/** Number of entries currently stored. */
size(): number;
/**
* Retrieve a snapshot of all current entries (read-only copy).
* Useful for serialisation or inspection.
*/
getEntries(): ReadonlyArray<Readonly<VaultEntry>>;
}
/**
* Secrets scanner — detects leaked credentials, API keys, and tokens.
*
* @module scanners/secrets
*/
/** Configuration for SecretsScanner. */
interface SecretsOptions {
/**
* When true, replace detected secrets with vault placeholders in
* `ScanResult.sanitized`. Requires `vault` to also be provided.
*/
redact?: boolean;
/**
* Vault instance used to store originals when `redact` is true.
* A vault must be provided to obtain unique, reversible placeholders.
* If `redact` is true but no vault is provided, each detected secret is
* replaced with the generic string `[REDACTED]` and cannot be restored.
*/
vault?: Vault;
/** Extra patterns merged with the built-in set. */
customPatterns?: SecretPattern[];
/**
* Minimum Shannon entropy required for patterns with `entropyCheck: true`.
* Default: 4.0
*/
minEntropy?: number;
/**
* When true (default), any detected secret causes a BLOCK decision.
* When false, the scanner will score proportionally (useful for HITL workflows).
*/
blockOnAny?: boolean;
/** Score threshold for BLOCK decision (default: 0.8). */
blockAt?: number;
/** Score threshold for HITL decision (default: 0.5). */
hitlAt?: number;
}
/**
* Scanner that detects secrets, API keys, and credentials in text.
*
* Implements the unified `Scanner` interface.
*/
declare class SecretsScanner implements Scanner {
/** @inheritdoc */
readonly name = "secrets";
private readonly compiledPatterns;
private readonly options;
constructor(options?: SecretsOptions);
/** @inheritdoc */
scan(input: string, _ctx?: ScanContext): ScanResult;
private redactInput;
}
/**
* Scan a single string for secrets using configurable options.
*
* @param input - The text to scan.
* @param options - Optional scanner configuration.
* @returns A `ScanResult` with decision, score, and match details.
*/
declare function scanSecrets(input: string, options?: SecretsOptions): ScanResult;
/**
* PII (Personally Identifiable Information) detection patterns.
*
* Pattern attribution:
* - ProtectAI/llm-guard (MIT) — https://github.com/protectai/llm-guard
* - Yelp/detect-secrets (Apache-2.0) — https://github.com/Yelp/detect-secrets
*
* Patterns have been reimplemented in TypeScript — no source was copied verbatim.
*
* @module scanners/patterns/pii-patterns
*/
/** Category of PII the pattern detects. */
type PiiType = 'email' | 'phone' | 'ssn' | 'credit_card' | 'iban' | 'ip' | 'mac' | 'postcode' | 'passport' | 'dob' | 'other';
/** A single PII detection rule. */
interface PiiPattern {
/** Unique identifier, e.g. "PII-EMAIL-001". */
id: string;
/** Category of PII. */
type: PiiType;
/** Compiled regular expression for detection. */
pattern: RegExp;
/**
* Optional post-match validator.
* If provided, a match only counts as PII when this returns true.
*/
validator?: (match: string) => boolean;
}
/**
* Luhn algorithm validator for credit card numbers.
* Strips spaces and dashes before checking.
*/
declare function luhnCheck(value: string): boolean;
/**
* Basic IBAN mod-97 structural validator.
* Does NOT check country-specific length rules; validates the mod-97 checksum.
*/
declare function ibanCheck(value: string): boolean;
/**
* All built-in PII detection patterns.
* Total: ≥25 patterns covering email, phone, SSN, credit card, IBAN, IP, MAC,
* postcodes, passports, and dates of birth.
*/
declare const PII_PATTERNS: PiiPattern[];
/**
* PII scanner — detects Personally Identifiable Information in text.
*
* @module scanners/pii
*/
/** Configuration for PiiScanner. */
interface PiiOptions {
/**
* When true, replace detected PII with vault placeholders in
* `ScanResult.sanitized`. Requires `vault` to also be provided.
*/
redact?: boolean;
/**
* Vault instance used to store originals when `redact` is true.
*/
vault?: Vault;
/** Extra patterns merged with the built-in set. */
customPatterns?: PiiPattern[];
/**
* When true (default), any detected PII causes a BLOCK decision.
* When false, the scanner will score proportionally.
*/
blockOnAny?: boolean;
/** Score threshold for BLOCK decision (default: 0.8). */
blockAt?: number;
/** Score threshold for HITL decision (default: 0.5). */
hitlAt?: number;
}
/**
* Scanner that detects PII (email, phone, SSN, credit card, IBAN, IP, MAC,
* postcodes, passports, and dates of birth) in text.
*
* Implements the unified `Scanner` interface.
*/
declare class PiiScanner implements Scanner {
/** @inheritdoc */
readonly name = "pii";
private readonly patterns;
private readonly options;
constructor(options?: PiiOptions);
/** @inheritdoc */
scan(input: string, _ctx?: ScanContext): ScanResult;
private redactInput;
}
/**
* Scan a single string for PII using configurable options.
*
* @param input - The text to scan.
* @param options - Optional scanner configuration.
* @returns A `ScanResult` with decision, score, and match details.
*/
declare function scanPii(input: string, options?: PiiOptions): ScanResult;
export { INJECTION_PATTERNS as I, PII_PATTERNS as P, Vault as V, PiiScanner as g, PromptInjectionScanner as j, SECRET_PATTERNS as k, SecretsScanner as r, allowResult as s, blockResult as t, checkPromptInjection as u, defaultPromptInjectionScanner as v, ibanCheck as w, luhnCheck as x, scanPii as y, scanSecrets as z };
export type { RestoreStrategy as R, ScanResult as S, ScanDecision as a, InjectionCategory as b, InjectionPattern as c, PatternSeverity as d, PiiOptions as e, PiiPattern as f, PiiType as h, PromptInjectionOptions as i, ScanContext as l, ScanMatch as m, Scanner as n, SecretPattern as o, SecretSeverity as p, SecretsOptions as q };