@graphorin/observability 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +10 -6
- package/dist/cost/cost-tracker.js.map +1 -1
- package/dist/cost/types.d.ts +1 -1
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval/types.d.ts +19 -1
- package/dist/eval/types.d.ts.map +1 -1
- package/dist/exporters/console.js.map +1 -1
- package/dist/exporters/jsonl.d.ts +1 -1
- package/dist/exporters/jsonl.js +2 -2
- package/dist/exporters/jsonl.js.map +1 -1
- package/dist/exporters/otlp-http.d.ts +2 -2
- package/dist/exporters/otlp-http.d.ts.map +1 -1
- package/dist/exporters/otlp-http.js +6 -4
- package/dist/exporters/otlp-http.js.map +1 -1
- package/dist/exporters/types.d.ts +2 -2
- package/dist/exporters/types.js +1 -1
- package/dist/exporters/types.js.map +1 -1
- package/dist/exporters/with-validation.d.ts +1 -1
- package/dist/exporters/with-validation.js.map +1 -1
- package/dist/gen-ai/emit.d.ts +1 -1
- package/dist/gen-ai/emit.js +1 -1
- package/dist/gen-ai/emit.js.map +1 -1
- package/dist/gen-ai/operation-mapping.d.ts +1 -1
- package/dist/gen-ai/operation-mapping.d.ts.map +1 -1
- package/dist/gen-ai/operation-mapping.js +3 -3
- package/dist/gen-ai/operation-mapping.js.map +1 -1
- package/dist/gen-ai/types.d.ts +2 -2
- package/dist/gen-ai/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/logger/logger.js +1 -1
- package/dist/logger/logger.js.map +1 -1
- package/dist/openinference/index.d.ts +2 -2
- package/dist/openinference/index.js +2 -2
- package/dist/openinference/index.js.map +1 -1
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/redaction/errors.js +1 -1
- package/dist/redaction/errors.js.map +1 -1
- package/dist/redaction/imperative-patterns.d.ts +3 -3
- package/dist/redaction/imperative-patterns.js +2 -2
- package/dist/redaction/imperative-patterns.js.map +1 -1
- package/dist/redaction/patterns.d.ts +6 -6
- package/dist/redaction/patterns.d.ts.map +1 -1
- package/dist/redaction/patterns.js +5 -5
- package/dist/redaction/patterns.js.map +1 -1
- package/dist/redaction/types.d.ts +1 -1
- package/dist/redaction/validator.js +2 -2
- package/dist/redaction/validator.js.map +1 -1
- package/dist/replay/config.d.ts +1 -1
- package/dist/replay/config.js.map +1 -1
- package/dist/replay/log.js +1 -1
- package/dist/replay/log.js.map +1 -1
- package/dist/replay/replay.js.map +1 -1
- package/dist/replay/types.d.ts +2 -2
- package/dist/telemetry/index.js +2 -2
- package/dist/telemetry/index.js.map +1 -1
- package/dist/tracer/sampling.d.ts +2 -2
- package/dist/tracer/sampling.js +1 -1
- package/dist/tracer/sampling.js.map +1 -1
- package/dist/tracer/span-names.d.ts +1 -1
- package/dist/tracer/span-names.d.ts.map +1 -1
- package/dist/tracer/span-names.js +25 -2
- package/dist/tracer/span-names.js.map +1 -1
- package/dist/tracer/span.js.map +1 -1
- package/dist/tracer/tracer.d.ts +1 -1
- package/dist/tracer/tracer.js +2 -2
- package/dist/tracer/tracer.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//#region src/gen-ai/operation-mapping.ts
|
|
2
2
|
const TABLE = Object.freeze([
|
|
3
|
-
["agent.run", "
|
|
4
|
-
["agent.step", "
|
|
3
|
+
["agent.run", "invoke_agent"],
|
|
4
|
+
["agent.step", "invoke_agent"],
|
|
5
5
|
["agent.handoff", "agent.handoff"],
|
|
6
6
|
["agent.suspend", "agent.suspend"],
|
|
7
7
|
["agent.resume", "agent.resume"],
|
|
@@ -54,7 +54,7 @@ function operationNameFor(type) {
|
|
|
54
54
|
return LOOKUP.get(type);
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
|
-
* Full canonical span-to-operation table
|
|
57
|
+
* Full canonical span-to-operation table - exposed for tooling
|
|
58
58
|
* (documentation generators, fixture tests) that need to introspect
|
|
59
59
|
* the mapping.
|
|
60
60
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operation-mapping.js","names":["TABLE: ReadonlyArray<readonly [SpanType, GenAIOperationName]>","OPERATION_NAME_TABLE: ReadonlyArray<readonly [SpanType, GenAIOperationName]>"],"sources":["../../src/gen-ai/operation-mapping.ts"],"sourcesContent":["/**\n * Mapping table from Graphorin `SpanType` values to canonical\n * `gen_ai.operation.name` enum values.\n *\n * The table mirrors the canonical-mapping reference table published\n * in the architecture documentation and is the authoritative source\n * for the `emitGenAIAttributes` helper.\n *\n * @packageDocumentation\n */\n\nimport type { SpanType } from '@graphorin/core';\n\nimport type { GenAIOperationName } from './types.js';\n\nconst TABLE: ReadonlyArray<readonly [SpanType, GenAIOperationName]> = Object.freeze([\n
|
|
1
|
+
{"version":3,"file":"operation-mapping.js","names":["TABLE: ReadonlyArray<readonly [SpanType, GenAIOperationName]>","OPERATION_NAME_TABLE: ReadonlyArray<readonly [SpanType, GenAIOperationName]>"],"sources":["../../src/gen-ai/operation-mapping.ts"],"sourcesContent":["/**\n * Mapping table from Graphorin `SpanType` values to canonical\n * `gen_ai.operation.name` enum values.\n *\n * The table mirrors the canonical-mapping reference table published\n * in the architecture documentation and is the authoritative source\n * for the `emitGenAIAttributes` helper.\n *\n * @packageDocumentation\n */\n\nimport type { SpanType } from '@graphorin/core';\n\nimport type { GenAIOperationName } from './types.js';\n\nconst TABLE: ReadonlyArray<readonly [SpanType, GenAIOperationName]> = Object.freeze([\n // E8: match what the runtime actually emits (agent factory stamps\n // `gen_ai.operation.name: 'invoke_agent'` on both run + step spans) and the\n // OTel GenAI operation enum, which has `invoke_agent` and no step concept.\n ['agent.run', 'invoke_agent'],\n ['agent.step', 'invoke_agent'],\n ['agent.handoff', 'agent.handoff'],\n ['agent.suspend', 'agent.suspend'],\n ['agent.resume', 'agent.resume'],\n\n ['provider.generate', 'chat'],\n ['provider.stream', 'chat'],\n\n ['tool.execute', 'execute_tool'],\n ['tool.approval', 'execute_tool'],\n\n ['memory.read.working', 'memory.read'],\n ['memory.read.session', 'memory.read'],\n ['memory.read.episodic', 'memory.read'],\n ['memory.read.semantic', 'memory.read'],\n ['memory.read.procedural', 'memory.read'],\n ['memory.read.shared', 'memory.read'],\n ['memory.write.working', 'memory.write'],\n ['memory.write.session', 'memory.write'],\n ['memory.write.episodic', 'memory.write'],\n ['memory.write.semantic', 'memory.write'],\n ['memory.write.procedural', 'memory.write'],\n ['memory.write.shared', 'memory.write'],\n ['memory.search.working', 'memory.search'],\n ['memory.search.session', 'memory.search'],\n ['memory.search.episodic', 'memory.search'],\n ['memory.search.semantic', 'memory.search'],\n ['memory.search.procedural', 'memory.search'],\n ['memory.search.shared', 'memory.search'],\n ['memory.consolidate.light', 'memory.consolidate'],\n ['memory.consolidate.standard', 'memory.consolidate'],\n ['memory.consolidate.deep', 'memory.consolidate'],\n ['memory.conflict', 'memory.conflict'],\n ['memory.embed', 'embedding'],\n\n ['workflow.run', 'workflow.run'],\n ['workflow.step', 'workflow.step'],\n ['workflow.task', 'workflow.task'],\n ['workflow.checkpoint', 'workflow.checkpoint'],\n\n ['skill.activate', 'skill.activate'],\n ['skill.load', 'skill.load'],\n\n ['mcp.connect', 'mcp.connect'],\n ['mcp.call', 'mcp.call'],\n ['mcp.list-tools', 'mcp.list-tools'],\n] as const);\n\nconst LOOKUP = new Map<SpanType, GenAIOperationName>(TABLE);\n\n/**\n * Resolve the canonical `gen_ai.operation.name` value for a Graphorin\n * span type. Returns `undefined` if no mapping exists (e.g. for\n * `replay.*` markers, which are emitted via a separate code path).\n *\n * @stable\n */\nexport function operationNameFor(type: SpanType): GenAIOperationName | undefined {\n return LOOKUP.get(type);\n}\n\n/**\n * Full canonical span-to-operation table - exposed for tooling\n * (documentation generators, fixture tests) that need to introspect\n * the mapping.\n *\n * @stable\n */\nexport const OPERATION_NAME_TABLE: ReadonlyArray<readonly [SpanType, GenAIOperationName]> = TABLE;\n"],"mappings":";AAeA,MAAMA,QAAgE,OAAO,OAAO;CAIlF,CAAC,aAAa,eAAe;CAC7B,CAAC,cAAc,eAAe;CAC9B,CAAC,iBAAiB,gBAAgB;CAClC,CAAC,iBAAiB,gBAAgB;CAClC,CAAC,gBAAgB,eAAe;CAEhC,CAAC,qBAAqB,OAAO;CAC7B,CAAC,mBAAmB,OAAO;CAE3B,CAAC,gBAAgB,eAAe;CAChC,CAAC,iBAAiB,eAAe;CAEjC,CAAC,uBAAuB,cAAc;CACtC,CAAC,uBAAuB,cAAc;CACtC,CAAC,wBAAwB,cAAc;CACvC,CAAC,wBAAwB,cAAc;CACvC,CAAC,0BAA0B,cAAc;CACzC,CAAC,sBAAsB,cAAc;CACrC,CAAC,wBAAwB,eAAe;CACxC,CAAC,wBAAwB,eAAe;CACxC,CAAC,yBAAyB,eAAe;CACzC,CAAC,yBAAyB,eAAe;CACzC,CAAC,2BAA2B,eAAe;CAC3C,CAAC,uBAAuB,eAAe;CACvC,CAAC,yBAAyB,gBAAgB;CAC1C,CAAC,yBAAyB,gBAAgB;CAC1C,CAAC,0BAA0B,gBAAgB;CAC3C,CAAC,0BAA0B,gBAAgB;CAC3C,CAAC,4BAA4B,gBAAgB;CAC7C,CAAC,wBAAwB,gBAAgB;CACzC,CAAC,4BAA4B,qBAAqB;CAClD,CAAC,+BAA+B,qBAAqB;CACrD,CAAC,2BAA2B,qBAAqB;CACjD,CAAC,mBAAmB,kBAAkB;CACtC,CAAC,gBAAgB,YAAY;CAE7B,CAAC,gBAAgB,eAAe;CAChC,CAAC,iBAAiB,gBAAgB;CAClC,CAAC,iBAAiB,gBAAgB;CAClC,CAAC,uBAAuB,sBAAsB;CAE9C,CAAC,kBAAkB,iBAAiB;CACpC,CAAC,cAAc,aAAa;CAE5B,CAAC,eAAe,cAAc;CAC9B,CAAC,YAAY,WAAW;CACxB,CAAC,kBAAkB,iBAAiB;CACrC,CAAU;AAEX,MAAM,SAAS,IAAI,IAAkC,MAAM;;;;;;;;AAS3D,SAAgB,iBAAiB,MAAgD;AAC/E,QAAO,OAAO,IAAI,KAAK;;;;;;;;;AAUzB,MAAaC,uBAA+E"}
|
package/dist/gen-ai/types.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ type GenAIToolType = 'function' | 'web_search' | 'database' | 'code_interpreter'
|
|
|
24
24
|
*
|
|
25
25
|
* @stable
|
|
26
26
|
*/
|
|
27
|
-
type GenAIOperationName = 'agent.run' | 'agent.step' | 'agent.handoff' | 'agent.suspend' | 'agent.resume' | 'agent.fanout.spawned' | 'agent.fanout.merged' | 'agent.evaluator.iteration' | 'chat' | 'embedding' | 'execute_tool' | 'memory.read' | 'memory.write' | 'memory.search' | 'memory.consolidate' | 'memory.conflict' | 'workflow.run' | 'workflow.step' | 'workflow.task' | 'workflow.checkpoint' | 'mcp.connect' | 'mcp.call' | 'mcp.list-tools' | 'skill.activate' | 'skill.load' | 'replay.run' | 'replay.skipped';
|
|
27
|
+
type GenAIOperationName = 'invoke_agent' | 'agent.run' | 'agent.step' | 'agent.handoff' | 'agent.suspend' | 'agent.resume' | 'agent.fanout.spawned' | 'agent.fanout.merged' | 'agent.evaluator.iteration' | 'chat' | 'embedding' | 'execute_tool' | 'memory.read' | 'memory.write' | 'memory.search' | 'memory.consolidate' | 'memory.conflict' | 'workflow.run' | 'workflow.step' | 'workflow.task' | 'workflow.checkpoint' | 'mcp.connect' | 'mcp.call' | 'mcp.list-tools' | 'skill.activate' | 'skill.load' | 'replay.run' | 'replay.skipped';
|
|
28
28
|
/**
|
|
29
29
|
* Per-message event type used by {@link emitGenAIMessageEvents}.
|
|
30
30
|
*
|
|
@@ -51,7 +51,7 @@ interface GenAIMessage {
|
|
|
51
51
|
/**
|
|
52
52
|
* Per-span attribute payload expected by {@link emitGenAIAttributes}.
|
|
53
53
|
* The fields mirror the OpenTelemetry GenAI semantic conventions and
|
|
54
|
-
* are merged with the existing Graphorin-prefixed attributes
|
|
54
|
+
* are merged with the existing Graphorin-prefixed attributes - the
|
|
55
55
|
* `gen_ai.*` family is additive, never replacing.
|
|
56
56
|
*
|
|
57
57
|
* @stable
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/gen-ai/types.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/gen-ai/types.ts"],"sourcesContent":[],"mappings":";;;;AA+FA;AAqBA;;;;;AAamC,KAlHvB,WAAA,GAkHuB,WAAA,GAAA,QAAA,GAAA,QAAA,GAAA,SAAA,GAAA,QAAA,GAAA,YAAA,GAAA,oBAAA,GAAA,aAAA,GAAA,QAAA,GAAA,WAAA,GAAA,oBAAA,GAAA,6BAAA,GAAA,CAAA,MAAA,GAAA;EAavB,SAAA,aAAA,CAAA,EAAA,KAAuB;;;;;;;;KA1GvB,aAAA;;;;;;;KAcA,kBAAA;;;;;;KAsCA,gBAAA;;;;;;UAOK,YAAA;iBACA;;;uBAGM;;;;;;;;;;;;;;;;UAiBN,eAAA;oBACG;;;;;;2BAMO;uBACJ;;;;;sBAKD;;;;;;;;;;;;KAaV,uBAAA,UAAiC,aAAa"}
|
package/dist/index.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ import { GraphorinTracer, TracerOptions, asGraphorinSpan, createTracer } from ".
|
|
|
32
32
|
|
|
33
33
|
//#region src/index.d.ts
|
|
34
34
|
/**
|
|
35
|
-
* @graphorin/observability
|
|
35
|
+
* @graphorin/observability - observability primitives for the
|
|
36
36
|
* Graphorin framework. Ships:
|
|
37
37
|
*
|
|
38
38
|
* - the typed `AISpan<T>` tracer over OpenTelemetry,
|
|
@@ -51,8 +51,7 @@ import { GraphorinTracer, TracerOptions, asGraphorinSpan, createTracer } from ".
|
|
|
51
51
|
*
|
|
52
52
|
* @packageDocumentation
|
|
53
53
|
*/
|
|
54
|
-
|
|
55
|
-
declare const VERSION = "0.5.0";
|
|
54
|
+
declare const VERSION: string;
|
|
56
55
|
//#endregion
|
|
57
56
|
export { ALL_BUILT_IN_PATTERNS, BUILT_IN_IMPERATIVE_PATTERNS, BUILT_IN_PATTERNS, BuiltInPatternName, Case, ConsoleExporterOptions, CostBudgetExceededCallback, CostBudgets, CostRecordInput, CostScope, CostSnapshot, CostTracker, CostTrackerOptions, DEFAULT_REPLAY_LOG_CONFIG, DEFAULT_VALIDATION_CONFIG, Dataset, EvalCaseResult, EvalReport, GenAIAttributes, GenAIMessage, GenAIMessageRole, GenAIOperationName, GenAISystem, GenAIToolType, GraphorinSpan, GraphorinTracer, IMPERATIVE_PREFILTER_SUBSTRINGS, ImperativePattern, ImperativePatternName, ScanResult as ImperativeScanResult, JSONLExporterOptions, LoggerFormat, LoggerOptions, OPEN_INFERENCE_EXCLUDED_TYPES, OPEN_INFERENCE_KIND_TABLE, OPERATION_NAME_TABLE, OPT_IN_PATTERNS, OTLPHttpExporterOptions, OpenInferenceSpanKind, PROVIDER_CLASS_TO_GEN_AI_SYSTEM, PatternCategory, PruneTracesOptions, RedactionCounters, RedactionInput, RedactionOutput, RedactionPattern, RedactionValidationError, RedactionValidator, RedactionValidatorInstance, RedactionValidatorOptions, RedactionViolation, RedactionViolationCallback, Replay, ReplayAuditBridge, ReplayAuditEvent, ReplayEvent, ReplayLogConfig, ReplayMode, ReplayOptions, ReplayRunInput, RunEvalOptions, Sampler, SamplingDecisionMaker, SamplingOptions, SamplingRule, ScoreResult, Scorer, SetAttributeOptions, SpanRecord, SpanRecordEvent, SpanTypeToOperationName, TelemetryStatus, TraceExporter, TracerOptions, UnvalidatedExporterError, VALIDATED_EXPORTER_BRAND, VERSION, ValidationConfig, WithValidationOptions, _resetGenAISystemWarningsForTesting, announceTelemetryPosture, asGraphorinSpan, compareSensitivityTiers, createConsoleExporter, createCostTracker, createJSONLExporter, createLogger, createOTLPHttpExporter, createRedactionValidator, createReplay, createSampler, createTracer, deriveGenAISystem, emitGenAIAttributes, emitGenAIMessageEvents, emitOpenInferenceKind, enableTelemetry, getCurrentSpanContext, getTelemetryStatus, getTraceLog, isValidatedExporter, newSpanId, newTraceId, openInferenceKindFor, operationNameFor, pruneTraces, runEval, scanImperativePatterns, setGenAISystemWarnSink, spanNameFor, stripImperativePatterns, tryGetValidatorCounters, withCurrentSpan, withValidation };
|
|
58
57
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAwBa"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { version } from "./package.js";
|
|
1
2
|
import { createCostTracker } from "./cost/cost-tracker.js";
|
|
2
3
|
import "./cost/index.js";
|
|
3
4
|
import { runEval } from "./eval/runner.js";
|
|
@@ -33,7 +34,7 @@ import "./tracer/index.js";
|
|
|
33
34
|
|
|
34
35
|
//#region src/index.ts
|
|
35
36
|
/**
|
|
36
|
-
* @graphorin/observability
|
|
37
|
+
* @graphorin/observability - observability primitives for the
|
|
37
38
|
* Graphorin framework. Ships:
|
|
38
39
|
*
|
|
39
40
|
* - the typed `AISpan<T>` tracer over OpenTelemetry,
|
|
@@ -52,8 +53,8 @@ import "./tracer/index.js";
|
|
|
52
53
|
*
|
|
53
54
|
* @packageDocumentation
|
|
54
55
|
*/
|
|
55
|
-
/** Canonical version constant
|
|
56
|
-
const VERSION =
|
|
56
|
+
/** Canonical version constant, derived from `package.json` at build time. */
|
|
57
|
+
const VERSION = version;
|
|
57
58
|
|
|
58
59
|
//#endregion
|
|
59
60
|
export { ALL_BUILT_IN_PATTERNS, BUILT_IN_IMPERATIVE_PATTERNS, BUILT_IN_PATTERNS, DEFAULT_REPLAY_LOG_CONFIG, DEFAULT_VALIDATION_CONFIG, IMPERATIVE_PREFILTER_SUBSTRINGS, OPEN_INFERENCE_EXCLUDED_TYPES, OPEN_INFERENCE_KIND_TABLE, OPERATION_NAME_TABLE, OPT_IN_PATTERNS, PROVIDER_CLASS_TO_GEN_AI_SYSTEM, RedactionValidationError, UnvalidatedExporterError, VALIDATED_EXPORTER_BRAND, VERSION, _resetGenAISystemWarningsForTesting, announceTelemetryPosture, asGraphorinSpan, compareSensitivityTiers, createConsoleExporter, createCostTracker, createJSONLExporter, createLogger, createOTLPHttpExporter, createRedactionValidator, createReplay, createSampler, createTracer, deriveGenAISystem, emitGenAIAttributes, emitGenAIMessageEvents, emitOpenInferenceKind, enableTelemetry, getCurrentSpanContext, getTelemetryStatus, getTraceLog, isValidatedExporter, newSpanId, newTraceId, openInferenceKindFor, operationNameFor, pruneTraces, runEval, scanImperativePatterns, setGenAISystemWarnSink, spanNameFor, stripImperativePatterns, tryGetValidatorCounters, withCurrentSpan, withValidation };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @graphorin/observability
|
|
1
|
+
{"version":3,"file":"index.js","names":["VERSION: string","pkg.version"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @graphorin/observability - observability primitives for the\n * Graphorin framework. Ships:\n *\n * - the typed `AISpan<T>` tracer over OpenTelemetry,\n * - the **mandatory** `withValidation()` wrapper for every exporter,\n * - the sensitivity-aware `RedactionValidator` with 14 built-in PII /\n * secret detection patterns,\n * - OpenTelemetry GenAI semantic-conventions conformance helpers,\n * - OpenInference span-kind emission,\n * - the structured `Logger` with span correlation,\n * - the append-only `JSONLExporter` for replay,\n * - sanitized-by-default `Replay` primitives,\n * - the hierarchical `CostTracker`,\n * - and a minimal inline eval runner.\n *\n * The full documentation lives in the package `README.md`.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant, derived from `package.json` at build time. */\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n\nexport * from './cost/index.js';\nexport * from './eval/index.js';\nexport * from './exporters/index.js';\nexport * from './gen-ai/index.js';\nexport * from './logger/index.js';\nexport * from './openinference/index.js';\nexport * from './redaction/index.js';\nexport * from './replay/index.js';\nexport * from './telemetry/index.js';\nexport * from './tracer/index.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAaA,UAAkBC"}
|
package/dist/logger/logger.js
CHANGED
|
@@ -9,7 +9,7 @@ const LEVELS = [
|
|
|
9
9
|
"error"
|
|
10
10
|
];
|
|
11
11
|
/**
|
|
12
|
-
* @internal
|
|
12
|
+
* @internal - context used to thread the current span id through nested
|
|
13
13
|
* async calls. Exposed via {@link withCurrentSpan} below.
|
|
14
14
|
*/
|
|
15
15
|
const SPAN_CONTEXT = createAsyncContext("graphorin.observability.current-span");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","names":["LEVELS: ReadonlyArray<LogLevel>","defaultTier: Sensitivity","payload: Record<string, unknown>","out: Record<string, unknown>"],"sources":["../../src/logger/logger.ts"],"sourcesContent":["/**\n * `createLogger(...)`
|
|
1
|
+
{"version":3,"file":"logger.js","names":["LEVELS: ReadonlyArray<LogLevel>","defaultTier: Sensitivity","payload: Record<string, unknown>","out: Record<string, unknown>"],"sources":["../../src/logger/logger.ts"],"sourcesContent":["/**\n * `createLogger(...)` - structured logger with optional span correlation.\n *\n * The logger is a thin wrapper around `console.{log,info,warn,error}`\n * that adds:\n *\n * - structured fields,\n * - JSON or pretty rendering,\n * - automatic correlation with the current Graphorin span (the\n * {@link CurrentSpanContext} helper carries the live trace + span ids),\n * - sensitivity-aware redaction via the supplied\n * {@link RedactionValidatorInstance}.\n *\n * @packageDocumentation\n */\n\nimport type { AISpan, LogFields, Logger, LogLevel, Sensitivity } from '@graphorin/core';\n\nimport { createAsyncContext } from '@graphorin/core';\n\nimport type { RedactionValidatorInstance } from '../redaction/types.js';\n\n/**\n * @stable\n */\nexport type LoggerFormat = 'json' | 'pretty';\n\n/**\n * Configuration shape for {@link createLogger}.\n *\n * @stable\n */\nexport interface LoggerOptions {\n readonly level?: LogLevel;\n readonly format?: LoggerFormat;\n /** Optional validator. Logger fields flow through `validate(...)`. */\n readonly redaction?: RedactionValidatorInstance;\n /** Default sensitivity tier for fields. Defaults to `'internal'`. */\n readonly defaultTier?: Sensitivity;\n /**\n * Sink that receives the rendered line. Defaults to writing to the\n * appropriate `console.*` method.\n */\n readonly sink?: (level: LogLevel, line: string) => void;\n}\n\nconst LEVELS: ReadonlyArray<LogLevel> = ['trace', 'debug', 'info', 'warn', 'error'] as const;\n\n/**\n * @internal - context used to thread the current span id through nested\n * async calls. Exposed via {@link withCurrentSpan} below.\n */\nconst SPAN_CONTEXT = createAsyncContext<{ traceId: string; spanId: string }>(\n 'graphorin.observability.current-span',\n);\n\n/**\n * Run `fn` with the supplied span as the \"current\" log-correlation\n * span. The logger picks up the trace + span ids automatically.\n *\n * @stable\n */\nexport function withCurrentSpan<R>(\n span: AISpan | undefined,\n fn: () => R | Promise<R>,\n): R | Promise<R> {\n if (span === undefined) return fn();\n return SPAN_CONTEXT.runAsync({ traceId: span.traceId, spanId: span.id }, async () => fn());\n}\n\n/**\n * Read the current span context (if any). Useful for callers that\n * want to attach span metadata to bespoke records.\n *\n * @stable\n */\nexport function getCurrentSpanContext():\n | { readonly traceId: string; readonly spanId: string }\n | undefined {\n return SPAN_CONTEXT.get();\n}\n\n/**\n * Build a {@link Logger} configured against the supplied options.\n *\n * @stable\n */\nexport function createLogger(opts: LoggerOptions = {}): Logger {\n const minLevel = LEVELS.indexOf(opts.level ?? 'info');\n const format = opts.format ?? 'json';\n const redaction = opts.redaction;\n const defaultTier: Sensitivity = opts.defaultTier ?? 'internal';\n const sink = opts.sink ?? defaultSink;\n\n function emit(level: LogLevel, message: string, fields?: LogFields): void {\n const idx = LEVELS.indexOf(level);\n if (idx < minLevel) return;\n\n const span = SPAN_CONTEXT.get();\n const safeFields = sanitizeFields(fields, redaction, defaultTier);\n\n const payload: Record<string, unknown> = {\n level,\n time: new Date().toISOString(),\n message,\n ...safeFields,\n };\n if (span !== undefined) {\n payload.traceId = span.traceId;\n payload.spanId = span.spanId;\n }\n\n const line = format === 'json' ? JSON.stringify(payload) : prettyRender(payload);\n sink(level, line);\n }\n\n return {\n trace: (message, fields) => emit('trace', message, fields),\n debug: (message, fields) => emit('debug', message, fields),\n info: (message, fields) => emit('info', message, fields),\n warn: (message, fields) => emit('warn', message, fields),\n error: (message, fields) => emit('error', message, fields),\n child(_bindings: LogFields): Logger {\n const inherited = sanitizeFields(_bindings, redaction, defaultTier);\n const parent = createLogger(opts);\n return {\n trace: (m, f) => parent.trace(m, { ...inherited, ...f }),\n debug: (m, f) => parent.debug(m, { ...inherited, ...f }),\n info: (m, f) => parent.info(m, { ...inherited, ...f }),\n warn: (m, f) => parent.warn(m, { ...inherited, ...f }),\n error: (m, f) => parent.error(m, { ...inherited, ...f }),\n child(b: LogFields): Logger {\n return parent.child({ ...inherited, ...b });\n },\n };\n },\n };\n}\n\nfunction sanitizeFields(\n fields: LogFields | undefined,\n validator: RedactionValidatorInstance | undefined,\n tier: Sensitivity,\n): LogFields {\n if (fields === undefined) return {};\n if (validator === undefined) return { ...fields };\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(fields)) {\n const result = validator.validate({ value: v, tier, context: { attribute: k } });\n if (result === null) continue;\n out[k] = result.value;\n }\n return out;\n}\n\nfunction defaultSink(level: LogLevel, line: string): void {\n switch (level) {\n case 'trace':\n case 'debug':\n console.debug(line);\n return;\n case 'info':\n console.info(line);\n return;\n case 'warn':\n console.warn(line);\n return;\n case 'error':\n console.error(line);\n return;\n }\n}\n\nfunction prettyRender(payload: Record<string, unknown>): string {\n const { level, time, message, ...rest } = payload;\n const fields = Object.keys(rest).length === 0 ? '' : ` ${JSON.stringify(rest)}`;\n return `[${String(time)}] ${String(level).toUpperCase()} ${String(message)}${fields}`;\n}\n"],"mappings":";;;AA8CA,MAAMA,SAAkC;CAAC;CAAS;CAAS;CAAQ;CAAQ;CAAQ;;;;;AAMnF,MAAM,eAAe,mBACnB,uCACD;;;;;;;AAQD,SAAgB,gBACd,MACA,IACgB;AAChB,KAAI,SAAS,OAAW,QAAO,IAAI;AACnC,QAAO,aAAa,SAAS;EAAE,SAAS,KAAK;EAAS,QAAQ,KAAK;EAAI,EAAE,YAAY,IAAI,CAAC;;;;;;;;AAS5F,SAAgB,wBAEF;AACZ,QAAO,aAAa,KAAK;;;;;;;AAQ3B,SAAgB,aAAa,OAAsB,EAAE,EAAU;CAC7D,MAAM,WAAW,OAAO,QAAQ,KAAK,SAAS,OAAO;CACrD,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,YAAY,KAAK;CACvB,MAAMC,cAA2B,KAAK,eAAe;CACrD,MAAM,OAAO,KAAK,QAAQ;CAE1B,SAAS,KAAK,OAAiB,SAAiB,QAA0B;AAExE,MADY,OAAO,QAAQ,MAAM,GACvB,SAAU;EAEpB,MAAM,OAAO,aAAa,KAAK;EAC/B,MAAM,aAAa,eAAe,QAAQ,WAAW,YAAY;EAEjE,MAAMC,UAAmC;GACvC;GACA,uBAAM,IAAI,MAAM,EAAC,aAAa;GAC9B;GACA,GAAG;GACJ;AACD,MAAI,SAAS,QAAW;AACtB,WAAQ,UAAU,KAAK;AACvB,WAAQ,SAAS,KAAK;;AAIxB,OAAK,OADQ,WAAW,SAAS,KAAK,UAAU,QAAQ,GAAG,aAAa,QAAQ,CAC/D;;AAGnB,QAAO;EACL,QAAQ,SAAS,WAAW,KAAK,SAAS,SAAS,OAAO;EAC1D,QAAQ,SAAS,WAAW,KAAK,SAAS,SAAS,OAAO;EAC1D,OAAO,SAAS,WAAW,KAAK,QAAQ,SAAS,OAAO;EACxD,OAAO,SAAS,WAAW,KAAK,QAAQ,SAAS,OAAO;EACxD,QAAQ,SAAS,WAAW,KAAK,SAAS,SAAS,OAAO;EAC1D,MAAM,WAA8B;GAClC,MAAM,YAAY,eAAe,WAAW,WAAW,YAAY;GACnE,MAAM,SAAS,aAAa,KAAK;AACjC,UAAO;IACL,QAAQ,GAAG,MAAM,OAAO,MAAM,GAAG;KAAE,GAAG;KAAW,GAAG;KAAG,CAAC;IACxD,QAAQ,GAAG,MAAM,OAAO,MAAM,GAAG;KAAE,GAAG;KAAW,GAAG;KAAG,CAAC;IACxD,OAAO,GAAG,MAAM,OAAO,KAAK,GAAG;KAAE,GAAG;KAAW,GAAG;KAAG,CAAC;IACtD,OAAO,GAAG,MAAM,OAAO,KAAK,GAAG;KAAE,GAAG;KAAW,GAAG;KAAG,CAAC;IACtD,QAAQ,GAAG,MAAM,OAAO,MAAM,GAAG;KAAE,GAAG;KAAW,GAAG;KAAG,CAAC;IACxD,MAAM,GAAsB;AAC1B,YAAO,OAAO,MAAM;MAAE,GAAG;MAAW,GAAG;MAAG,CAAC;;IAE9C;;EAEJ;;AAGH,SAAS,eACP,QACA,WACA,MACW;AACX,KAAI,WAAW,OAAW,QAAO,EAAE;AACnC,KAAI,cAAc,OAAW,QAAO,EAAE,GAAG,QAAQ;CACjD,MAAMC,MAA+B,EAAE;AACvC,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,EAAE;EAC3C,MAAM,SAAS,UAAU,SAAS;GAAE,OAAO;GAAG;GAAM,SAAS,EAAE,WAAW,GAAG;GAAE,CAAC;AAChF,MAAI,WAAW,KAAM;AACrB,MAAI,KAAK,OAAO;;AAElB,QAAO;;AAGT,SAAS,YAAY,OAAiB,MAAoB;AACxD,SAAQ,OAAR;EACE,KAAK;EACL,KAAK;AACH,WAAQ,MAAM,KAAK;AACnB;EACF,KAAK;AACH,WAAQ,KAAK,KAAK;AAClB;EACF,KAAK;AACH,WAAQ,KAAK,KAAK;AAClB;EACF,KAAK;AACH,WAAQ,MAAM,KAAK;AACnB;;;AAIN,SAAS,aAAa,SAA0C;CAC9D,MAAM,EAAE,OAAO,MAAM,SAAS,GAAG,SAAS;CAC1C,MAAM,SAAS,OAAO,KAAK,KAAK,CAAC,WAAW,IAAI,KAAK,IAAI,KAAK,UAAU,KAAK;AAC7E,QAAO,IAAI,OAAO,KAAK,CAAC,IAAI,OAAO,MAAM,CAAC,aAAa,CAAC,GAAG,OAAO,QAAQ,GAAG"}
|
|
@@ -10,7 +10,7 @@ import { AISpan, SpanType } from "@graphorin/core";
|
|
|
10
10
|
type OpenInferenceSpanKind = 'AGENT' | 'EVALUATOR' | 'LLM' | 'TOOL' | 'RETRIEVER' | 'EMBEDDING' | 'CHAIN' | 'RERANKER';
|
|
11
11
|
/**
|
|
12
12
|
* Span types intentionally excluded from OpenInference span-kind
|
|
13
|
-
* emission per the canonical table
|
|
13
|
+
* emission per the canonical table - `skill.*`, `mcp.connect`,
|
|
14
14
|
* `mcp.list-tools`, and `replay.*` markers do not have a clean
|
|
15
15
|
* OpenInference equivalent.
|
|
16
16
|
*
|
|
@@ -34,7 +34,7 @@ declare function openInferenceKindFor<T extends SpanType>(type: T): OpenInferenc
|
|
|
34
34
|
*/
|
|
35
35
|
declare function emitOpenInferenceKind<T extends SpanType>(span: AISpan<T>): void;
|
|
36
36
|
/**
|
|
37
|
-
* Full canonical span-to-kind table
|
|
37
|
+
* Full canonical span-to-kind table - exposed for tooling and tests
|
|
38
38
|
* that need to introspect the mapping.
|
|
39
39
|
*
|
|
40
40
|
* @stable
|
|
@@ -42,7 +42,7 @@ const KIND_TABLE = Object.freeze([
|
|
|
42
42
|
]);
|
|
43
43
|
/**
|
|
44
44
|
* Span types intentionally excluded from OpenInference span-kind
|
|
45
|
-
* emission per the canonical table
|
|
45
|
+
* emission per the canonical table - `skill.*`, `mcp.connect`,
|
|
46
46
|
* `mcp.list-tools`, and `replay.*` markers do not have a clean
|
|
47
47
|
* OpenInference equivalent.
|
|
48
48
|
*
|
|
@@ -80,7 +80,7 @@ function emitOpenInferenceKind(span) {
|
|
|
80
80
|
else span.setAttributes({ "openinference.span.kind": kind });
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
|
-
* Full canonical span-to-kind table
|
|
83
|
+
* Full canonical span-to-kind table - exposed for tooling and tests
|
|
84
84
|
* that need to introspect the mapping.
|
|
85
85
|
*
|
|
86
86
|
* @stable
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["KIND_TABLE: ReadonlyArray<readonly [SpanType, OpenInferenceSpanKind]>","OPEN_INFERENCE_EXCLUDED_TYPES: ReadonlyArray<SpanType>"],"sources":["../../src/openinference/index.ts"],"sourcesContent":["/**\n * OpenInference span-kind emission. Adds the\n * `openinference.span.kind` attribute (one of `AGENT`, `EVALUATOR`,\n * `LLM`, `TOOL`, `RETRIEVER`, `EMBEDDING`, `CHAIN`) to applicable\n * Graphorin spans.\n *\n * The mapping is the canonical table published in the architecture\n * documentation. Span types without a clean OpenInference equivalent\n * (`skill.*`, `mcp.connect`, `mcp.list-tools`, `replay.*`) are NOT\n * emitted
|
|
1
|
+
{"version":3,"file":"index.js","names":["KIND_TABLE: ReadonlyArray<readonly [SpanType, OpenInferenceSpanKind]>","OPEN_INFERENCE_EXCLUDED_TYPES: ReadonlyArray<SpanType>"],"sources":["../../src/openinference/index.ts"],"sourcesContent":["/**\n * OpenInference span-kind emission. Adds the\n * `openinference.span.kind` attribute (one of `AGENT`, `EVALUATOR`,\n * `LLM`, `TOOL`, `RETRIEVER`, `EMBEDDING`, `CHAIN`) to applicable\n * Graphorin spans.\n *\n * The mapping is the canonical table published in the architecture\n * documentation. Span types without a clean OpenInference equivalent\n * (`skill.*`, `mcp.connect`, `mcp.list-tools`, `replay.*`) are NOT\n * emitted - the caller can introspect via {@link openInferenceKindFor}\n * and decide whether to log a fallback attribute.\n *\n * @packageDocumentation\n */\n\nimport type { AISpan, SpanType } from '@graphorin/core';\n\nimport { asGraphorinSpan } from '../tracer/tracer.js';\n\n/**\n * Canonical OpenInference span-kind enum.\n *\n * @stable\n */\nexport type OpenInferenceSpanKind =\n | 'AGENT'\n | 'EVALUATOR'\n | 'LLM'\n | 'TOOL'\n | 'RETRIEVER'\n | 'EMBEDDING'\n | 'CHAIN'\n | 'RERANKER';\n\nconst KIND_TABLE: ReadonlyArray<readonly [SpanType, OpenInferenceSpanKind]> = Object.freeze([\n ['agent.run', 'AGENT'],\n ['agent.step', 'AGENT'],\n ['agent.handoff', 'AGENT'],\n ['agent.suspend', 'AGENT'],\n ['agent.resume', 'AGENT'],\n\n ['provider.generate', 'LLM'],\n ['provider.stream', 'LLM'],\n\n ['tool.execute', 'TOOL'],\n ['tool.approval', 'TOOL'],\n\n ['memory.read.working', 'RETRIEVER'],\n ['memory.read.session', 'RETRIEVER'],\n ['memory.read.episodic', 'RETRIEVER'],\n ['memory.read.semantic', 'RETRIEVER'],\n ['memory.read.procedural', 'RETRIEVER'],\n ['memory.read.shared', 'RETRIEVER'],\n ['memory.write.working', 'RETRIEVER'],\n ['memory.write.session', 'RETRIEVER'],\n ['memory.write.episodic', 'RETRIEVER'],\n ['memory.write.semantic', 'RETRIEVER'],\n ['memory.write.procedural', 'RETRIEVER'],\n ['memory.write.shared', 'RETRIEVER'],\n ['memory.search.working', 'RETRIEVER'],\n ['memory.search.session', 'RETRIEVER'],\n ['memory.search.episodic', 'RETRIEVER'],\n ['memory.search.semantic', 'RETRIEVER'],\n ['memory.search.procedural', 'RETRIEVER'],\n ['memory.search.shared', 'RETRIEVER'],\n\n ['memory.embed', 'EMBEDDING'],\n\n ['memory.consolidate.light', 'CHAIN'],\n ['memory.consolidate.standard', 'CHAIN'],\n ['memory.consolidate.deep', 'CHAIN'],\n ['memory.conflict', 'CHAIN'],\n ['workflow.run', 'CHAIN'],\n ['workflow.step', 'CHAIN'],\n ['workflow.task', 'CHAIN'],\n ['workflow.checkpoint', 'CHAIN'],\n\n ['mcp.call', 'TOOL'],\n] as const);\n\n/**\n * Span types intentionally excluded from OpenInference span-kind\n * emission per the canonical table - `skill.*`, `mcp.connect`,\n * `mcp.list-tools`, and `replay.*` markers do not have a clean\n * OpenInference equivalent.\n *\n * @stable\n */\nexport const OPEN_INFERENCE_EXCLUDED_TYPES: ReadonlyArray<SpanType> = Object.freeze([\n 'skill.activate',\n 'skill.load',\n 'mcp.connect',\n 'mcp.list-tools',\n] as const);\n\nconst KIND_LOOKUP = new Map<SpanType, OpenInferenceSpanKind>(KIND_TABLE);\n\n/**\n * Resolve the OpenInference span kind for a Graphorin span type.\n * Returns `null` for types intentionally excluded from emission.\n *\n * @stable\n */\nexport function openInferenceKindFor<T extends SpanType>(type: T): OpenInferenceSpanKind | null {\n return KIND_LOOKUP.get(type) ?? null;\n}\n\n/**\n * Attach the `openinference.span.kind` attribute to a span. No-op for\n * span types that lack a clean OpenInference equivalent. The attribute\n * is tagged `'public'` because the enum value is bounded and contains\n * no PII.\n *\n * @stable\n */\nexport function emitOpenInferenceKind<T extends SpanType>(span: AISpan<T>): void {\n const kind = KIND_LOOKUP.get(span.type);\n if (kind === undefined) return;\n const gs = asGraphorinSpan(span);\n if (gs !== null) {\n gs.setAttribute('openinference.span.kind', kind, { sensitivity: 'public' });\n } else {\n span.setAttributes({ 'openinference.span.kind': kind });\n }\n}\n\n/**\n * Full canonical span-to-kind table - exposed for tooling and tests\n * that need to introspect the mapping.\n *\n * @stable\n */\nexport const OPEN_INFERENCE_KIND_TABLE = KIND_TABLE;\n"],"mappings":";;;AAkCA,MAAMA,aAAwE,OAAO,OAAO;CAC1F,CAAC,aAAa,QAAQ;CACtB,CAAC,cAAc,QAAQ;CACvB,CAAC,iBAAiB,QAAQ;CAC1B,CAAC,iBAAiB,QAAQ;CAC1B,CAAC,gBAAgB,QAAQ;CAEzB,CAAC,qBAAqB,MAAM;CAC5B,CAAC,mBAAmB,MAAM;CAE1B,CAAC,gBAAgB,OAAO;CACxB,CAAC,iBAAiB,OAAO;CAEzB,CAAC,uBAAuB,YAAY;CACpC,CAAC,uBAAuB,YAAY;CACpC,CAAC,wBAAwB,YAAY;CACrC,CAAC,wBAAwB,YAAY;CACrC,CAAC,0BAA0B,YAAY;CACvC,CAAC,sBAAsB,YAAY;CACnC,CAAC,wBAAwB,YAAY;CACrC,CAAC,wBAAwB,YAAY;CACrC,CAAC,yBAAyB,YAAY;CACtC,CAAC,yBAAyB,YAAY;CACtC,CAAC,2BAA2B,YAAY;CACxC,CAAC,uBAAuB,YAAY;CACpC,CAAC,yBAAyB,YAAY;CACtC,CAAC,yBAAyB,YAAY;CACtC,CAAC,0BAA0B,YAAY;CACvC,CAAC,0BAA0B,YAAY;CACvC,CAAC,4BAA4B,YAAY;CACzC,CAAC,wBAAwB,YAAY;CAErC,CAAC,gBAAgB,YAAY;CAE7B,CAAC,4BAA4B,QAAQ;CACrC,CAAC,+BAA+B,QAAQ;CACxC,CAAC,2BAA2B,QAAQ;CACpC,CAAC,mBAAmB,QAAQ;CAC5B,CAAC,gBAAgB,QAAQ;CACzB,CAAC,iBAAiB,QAAQ;CAC1B,CAAC,iBAAiB,QAAQ;CAC1B,CAAC,uBAAuB,QAAQ;CAEhC,CAAC,YAAY,OAAO;CACrB,CAAU;;;;;;;;;AAUX,MAAaC,gCAAyD,OAAO,OAAO;CAClF;CACA;CACA;CACA;CACD,CAAU;AAEX,MAAM,cAAc,IAAI,IAAqC,WAAW;;;;;;;AAQxE,SAAgB,qBAAyC,MAAuC;AAC9F,QAAO,YAAY,IAAI,KAAK,IAAI;;;;;;;;;;AAWlC,SAAgB,sBAA0C,MAAuB;CAC/E,MAAM,OAAO,YAAY,IAAI,KAAK,KAAK;AACvC,KAAI,SAAS,OAAW;CACxB,MAAM,KAAK,gBAAgB,KAAK;AAChC,KAAI,OAAO,KACT,IAAG,aAAa,2BAA2B,MAAM,EAAE,aAAa,UAAU,CAAC;KAE3E,MAAK,cAAc,EAAE,2BAA2B,MAAM,CAAC;;;;;;;;AAU3D,MAAa,4BAA4B"}
|
package/dist/package.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package.js","names":[],"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"@graphorin/observability\",\n \"version\": \"0.6.1\",\n \"description\": \"Observability primitives for the Graphorin framework: typed AISpan tracer over OpenTelemetry, mandatory withValidation wrapper for every exporter, sensitivity-aware RedactionValidator with built-in PII / secret patterns, GenAI Semantic Conventions conformance helpers, OpenInference span-kind emission, structured logger with span correlation, append-only JSONL exporter for replay, sanitized replay primitives, hierarchical CostTracker, and a minimal inline eval runner alongside a zero-default telemetry stub.\",\n \"license\": \"MIT\",\n \"author\": \"Oleksiy Stepurenko\",\n \"homepage\": \"https://github.com/o-stepper/graphorin/tree/main/packages/observability\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/o-stepper/graphorin.git\",\n \"directory\": \"packages/observability\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/o-stepper/graphorin/issues\"\n },\n \"keywords\": [\n \"graphorin\",\n \"ai\",\n \"agents\",\n \"framework\",\n \"observability\",\n \"tracing\",\n \"opentelemetry\",\n \"otel\",\n \"gen-ai\",\n \"openinference\",\n \"redaction\",\n \"pii\",\n \"cost-tracking\",\n \"replay\",\n \"evals\",\n \"logger\"\n ],\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./tracer\": {\n \"types\": \"./dist/tracer/index.d.ts\",\n \"import\": \"./dist/tracer/index.js\"\n },\n \"./redaction\": {\n \"types\": \"./dist/redaction/index.d.ts\",\n \"import\": \"./dist/redaction/index.js\"\n },\n \"./redaction/patterns\": {\n \"types\": \"./dist/redaction/patterns.d.ts\",\n \"import\": \"./dist/redaction/patterns.js\"\n },\n \"./redaction/imperative-patterns\": {\n \"types\": \"./dist/redaction/imperative-patterns.d.ts\",\n \"import\": \"./dist/redaction/imperative-patterns.js\"\n },\n \"./exporters\": {\n \"types\": \"./dist/exporters/index.d.ts\",\n \"import\": \"./dist/exporters/index.js\"\n },\n \"./gen-ai\": {\n \"types\": \"./dist/gen-ai/index.d.ts\",\n \"import\": \"./dist/gen-ai/index.js\"\n },\n \"./openinference\": {\n \"types\": \"./dist/openinference/index.d.ts\",\n \"import\": \"./dist/openinference/index.js\"\n },\n \"./cost\": {\n \"types\": \"./dist/cost/index.d.ts\",\n \"import\": \"./dist/cost/index.js\"\n },\n \"./replay\": {\n \"types\": \"./dist/replay/index.d.ts\",\n \"import\": \"./dist/replay/index.js\"\n },\n \"./eval\": {\n \"types\": \"./dist/eval/index.d.ts\",\n \"import\": \"./dist/eval/index.js\"\n },\n \"./logger\": {\n \"types\": \"./dist/logger/index.d.ts\",\n \"import\": \"./dist/logger/index.js\"\n },\n \"./telemetry\": {\n \"types\": \"./dist/telemetry/index.d.ts\",\n \"import\": \"./dist/telemetry/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"CHANGELOG.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit && tsc -p tsconfig.tests.json\",\n \"test\": \"vitest run\",\n \"lint\": \"biome check .\",\n \"clean\": \"rimraf dist .turbo *.tsbuildinfo\"\n },\n \"dependencies\": {\n \"@graphorin/core\": \"workspace:*\"\n },\n \"peerDependencies\": {\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/sdk-node\": \"^0.217.0\",\n \"@opentelemetry/exporter-trace-otlp-http\": \"^0.215.0\"\n },\n \"peerDependenciesMeta\": {\n \"@opentelemetry/api\": {\n \"optional\": false\n },\n \"@opentelemetry/sdk-node\": {\n \"optional\": true\n },\n \"@opentelemetry/exporter-trace-otlp-http\": {\n \"optional\": true\n }\n },\n \"publishConfig\": {\n \"access\": \"public\",\n \"provenance\": true\n },\n \"devDependencies\": {\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/exporter-trace-otlp-http\": \"^0.215.0\",\n \"@opentelemetry/sdk-node\": \"^0.217.0\",\n \"fast-check\": \"^3.23.0\"\n }\n}\n"],"mappings":";cAEa"}
|
package/dist/redaction/errors.js
CHANGED
|
@@ -31,7 +31,7 @@ var UnvalidatedExporterError = class extends Error {
|
|
|
31
31
|
/** Exporter identifier (typically `exporter.constructor.name`). */
|
|
32
32
|
exporterId;
|
|
33
33
|
constructor(exporterId) {
|
|
34
|
-
super(`Exporter "${exporterId}" was registered without withValidation(). All exporters must be wrapped
|
|
34
|
+
super(`Exporter "${exporterId}" was registered without withValidation(). All exporters must be wrapped - see RedactionValidator policy. Wrap with \`withValidation(exporter, opts)\` or set \`validation: 'off'\` (NOT recommended) on the tracer to opt out explicitly (logs a startup WARN).`);
|
|
35
35
|
this.name = "UnvalidatedExporterError";
|
|
36
36
|
this.exporterId = exporterId;
|
|
37
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../../src/redaction/errors.ts"],"sourcesContent":["/**\n * Errors thrown by the redaction layer.\n *\n * @packageDocumentation\n */\n\nimport type { RedactionViolation } from './types.js';\n\n/**\n * Thrown by the validator when `failOnUnredactedSensitive: true` and\n * a value would otherwise be dropped or masked. The error never\n * carries the secret value itself; only the sanitized\n * {@link RedactionViolation} metadata.\n *\n * @stable\n */\nexport class RedactionValidationError extends Error {\n /** @stable */\n readonly kind = 'redaction-validation-failed' as const;\n /** @stable */\n readonly violation: RedactionViolation;\n\n constructor(message: string, violation: RedactionViolation) {\n super(message);\n this.name = 'RedactionValidationError';\n this.violation = violation;\n }\n}\n\n/**\n * Thrown at startup when an exporter is registered without going\n * through `withValidation(...)`. Enforces ADR-035: every exporter\n * must validate before forwarding.\n *\n * @stable\n */\nexport class UnvalidatedExporterError extends Error {\n /** @stable */\n readonly kind = 'unvalidated-exporter' as const;\n /** Exporter identifier (typically `exporter.constructor.name`). */\n readonly exporterId: string;\n\n constructor(exporterId: string) {\n super(\n `Exporter \"${exporterId}\" was registered without withValidation(). All ` +\n 'exporters must be wrapped
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../../src/redaction/errors.ts"],"sourcesContent":["/**\n * Errors thrown by the redaction layer.\n *\n * @packageDocumentation\n */\n\nimport type { RedactionViolation } from './types.js';\n\n/**\n * Thrown by the validator when `failOnUnredactedSensitive: true` and\n * a value would otherwise be dropped or masked. The error never\n * carries the secret value itself; only the sanitized\n * {@link RedactionViolation} metadata.\n *\n * @stable\n */\nexport class RedactionValidationError extends Error {\n /** @stable */\n readonly kind = 'redaction-validation-failed' as const;\n /** @stable */\n readonly violation: RedactionViolation;\n\n constructor(message: string, violation: RedactionViolation) {\n super(message);\n this.name = 'RedactionValidationError';\n this.violation = violation;\n }\n}\n\n/**\n * Thrown at startup when an exporter is registered without going\n * through `withValidation(...)`. Enforces ADR-035: every exporter\n * must validate before forwarding.\n *\n * @stable\n */\nexport class UnvalidatedExporterError extends Error {\n /** @stable */\n readonly kind = 'unvalidated-exporter' as const;\n /** Exporter identifier (typically `exporter.constructor.name`). */\n readonly exporterId: string;\n\n constructor(exporterId: string) {\n super(\n `Exporter \"${exporterId}\" was registered without withValidation(). All ` +\n 'exporters must be wrapped - see RedactionValidator policy. Wrap with ' +\n \"`withValidation(exporter, opts)` or set `validation: 'off'` (NOT recommended) \" +\n 'on the tracer to opt out explicitly (logs a startup WARN).',\n );\n this.name = 'UnvalidatedExporterError';\n this.exporterId = exporterId;\n }\n}\n"],"mappings":";;;;;;;;;AAgBA,IAAa,2BAAb,cAA8C,MAAM;;CAElD,AAAS,OAAO;;CAEhB,AAAS;CAET,YAAY,SAAiB,WAA+B;AAC1D,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,YAAY;;;;;;;;;;AAWrB,IAAa,2BAAb,cAA8C,MAAM;;CAElD,AAAS,OAAO;;CAEhB,AAAS;CAET,YAAY,YAAoB;AAC9B,QACE,aAAa,WAAW,kQAIzB;AACD,OAAK,OAAO;AACZ,OAAK,aAAa"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Imperative-pattern catalogue for inbound prompt-injection defence.
|
|
4
4
|
*
|
|
5
|
-
* Sibling to {@link BUILT_IN_PATTERNS} (PII / secrets)
|
|
5
|
+
* Sibling to {@link BUILT_IN_PATTERNS} (PII / secrets) - the two
|
|
6
6
|
* catalogues are disjoint by construction. The imperative catalogue
|
|
7
7
|
* is consumed by the inbound sanitization layer in `@graphorin/tools`
|
|
8
8
|
* to scan tool / MCP results before they reach the agent's message
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* exporter.
|
|
12
12
|
*
|
|
13
13
|
* The patterns target the canonical English "ignore previous
|
|
14
|
-
* instructions" / "system override" injection family
|
|
14
|
+
* instructions" / "system override" injection family - the concrete
|
|
15
15
|
* surface that an untrusted-skill or MCP server result might use to
|
|
16
16
|
* smuggle imperative content into the next provider call. The
|
|
17
17
|
* catalogue is intentionally conservative: every entry has a fixed-
|
|
@@ -73,7 +73,7 @@ declare const IMPERATIVE_PREFILTER_SUBSTRINGS: ReadonlyArray<string>;
|
|
|
73
73
|
/**
|
|
74
74
|
* Compiled scan helper. Returns the list of pattern names that fired
|
|
75
75
|
* AND the number of bytes the strip would remove if applied. Bounded
|
|
76
|
-
* by the budget hint
|
|
76
|
+
* by the budget hint - when exceeded, returns `null` to let the caller
|
|
77
77
|
* apply the best-effort `'detect-failed'` annotation.
|
|
78
78
|
*
|
|
79
79
|
* @stable
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const PATTERNS = [
|
|
3
3
|
{
|
|
4
4
|
name: "ignore-previous-instructions",
|
|
5
|
-
description: "The canonical \"ignore previous instructions\" injection
|
|
5
|
+
description: "The canonical \"ignore previous instructions\" injection - the most common form of prompt injection seen across MCP results.",
|
|
6
6
|
prefilter: [
|
|
7
7
|
"ignore",
|
|
8
8
|
"disregard",
|
|
@@ -61,7 +61,7 @@ const PATTERNS = [
|
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
name: "developer-mode",
|
|
64
|
-
description: "Requests to enter \"developer mode\" / \"DAN\" / unrestricted modes
|
|
64
|
+
description: "Requests to enter \"developer mode\" / \"DAN\" / unrestricted modes - a long-running jailbreak family.",
|
|
65
65
|
prefilter: [
|
|
66
66
|
"developer mode",
|
|
67
67
|
"admin mode",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imperative-patterns.js","names":["PATTERNS: readonly ImperativePattern[]","BUILT_IN_IMPERATIVE_PATTERNS: readonly ImperativePattern[]","IMPERATIVE_PREFILTER_SUBSTRINGS: ReadonlyArray<string>","hits: { pattern: string; matchCount: number }[]","match: RegExpExecArray | null"],"sources":["../../src/redaction/imperative-patterns.ts"],"sourcesContent":["/**\n * Imperative-pattern catalogue for inbound prompt-injection defence.\n *\n * Sibling to {@link BUILT_IN_PATTERNS} (PII / secrets) — the two\n * catalogues are disjoint by construction. The imperative catalogue\n * is consumed by the inbound sanitization layer in `@graphorin/tools`\n * to scan tool / MCP results before they reach the agent's message\n * store; the PII / secrets catalogue is consumed by the outbound\n * exporter validators to scan span attributes before they reach an\n * exporter.\n *\n * The patterns target the canonical English \"ignore previous\n * instructions\" / \"system override\" injection family — the concrete\n * surface that an untrusted-skill or MCP server result might use to\n * smuggle imperative content into the next provider call. The\n * catalogue is intentionally conservative: every entry has a fixed-\n * substring prefilter so the per-byte scan budget stays sub-millisecond\n * on typical 16 KB tool results.\n *\n * @packageDocumentation\n */\n\n/**\n * Stable name of an imperative pattern. The catalogue is curated;\n * user-supplied patterns can use any identifier they want and will be\n * passed through the sanitization layer alongside the built-ins.\n *\n * @stable\n */\nexport type ImperativePatternName =\n | 'ignore-previous-instructions'\n | 'forget-instructions'\n | 'override-instructions'\n | 'system-prompt-leak'\n | 'role-reassignment'\n | 'developer-mode'\n | 'jailbreak-marker'\n | 'tool-call-injection'\n | 'role-tag-injection';\n\n/**\n * One entry in the imperative-pattern catalogue. The shape mirrors\n * {@link BUILT_IN_PATTERNS} so consumers can share scan / replace\n * machinery, but the fields are typed as imperative-only so the two\n * catalogues do not accidentally merge.\n *\n * @stable\n */\nexport interface ImperativePattern {\n readonly name: ImperativePatternName | (string & {});\n readonly description: string;\n /**\n * Cheap substring prefilter applied before the regex; if the body\n * does not contain any of the prefilter substrings the regex is\n * skipped entirely. The prefilter is case-insensitive.\n */\n readonly prefilter: ReadonlyArray<string>;\n /**\n * Full regex applied when the prefilter matches. Always carries the\n * `g` and `i` flags; the catalogue construction validates this.\n */\n readonly regex: RegExp;\n /** Replacement string applied by the `'detect-and-strip*'` policies. */\n readonly mask: string;\n}\n\nconst PATTERNS: readonly ImperativePattern[] = [\n {\n name: 'ignore-previous-instructions',\n description:\n 'The canonical \"ignore previous instructions\" injection — the most common form of prompt injection seen across MCP results.',\n prefilter: ['ignore', 'disregard', 'forget'],\n regex:\n /\\b(?:ignore|disregard|forget)\\s+(?:all\\s+)?(?:the\\s+)?(?:previous|prior|above|earlier|preceding|original)\\s+(?:instructions?|prompts?|messages?|directives?|rules?)\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'forget-instructions',\n description: 'Imperative \"forget what I told you\" / memory-wipe family.',\n prefilter: ['forget', 'erase', 'clear'],\n regex:\n /\\b(?:forget|erase|clear|wipe)\\s+(?:everything|all)\\s+(?:above|before|previous|i\\s+(?:told|said))\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'override-instructions',\n description: '\"Override the system prompt\" / \"new instructions\" injection family.',\n prefilter: ['override', 'new instructions', 'updated instructions', 'replace'],\n regex:\n /\\b(?:override|replace)\\s+(?:the\\s+)?(?:system\\s+)?(?:prompt|instructions?|directives?)\\b|\\bnew\\s+(?:instructions?|prompts?|directives?)\\s*(?::|follow|are)\\b|\\bupdated\\s+(?:instructions?|prompts?|directives?)\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'system-prompt-leak',\n description:\n 'Requests asking the model to reveal its system prompt / hidden instructions / configuration.',\n prefilter: ['system prompt', 'system message', 'reveal', 'print', 'show'],\n regex:\n /\\b(?:reveal|print|show|output|repeat|expose|disclose)\\s+(?:your|the)\\s+(?:system\\s+(?:prompt|message|instructions?)|hidden\\s+instructions?|initial\\s+(?:prompt|instructions?)|configuration)\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'role-reassignment',\n description: '\"You are now ...\" / role-reassignment imperative family.',\n prefilter: ['you are now', 'you are no longer', 'pretend', 'act as'],\n regex:\n /\\byou\\s+are\\s+(?:now|no\\s+longer)\\s+(?:a\\b|an\\b|the\\b)|\\bpretend\\s+(?:to\\s+be|you\\s+are)\\b|\\bact\\s+as\\s+(?:a|an|the|if\\s+you)\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'developer-mode',\n description:\n 'Requests to enter \"developer mode\" / \"DAN\" / unrestricted modes — a long-running jailbreak family.',\n prefilter: ['developer mode', 'admin mode', 'unrestricted'],\n regex:\n /\\b(?:enter|enable|activate)\\s+(?:developer|admin|debug|unrestricted|god|root)\\s+mode\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'jailbreak-marker',\n description: 'Direct \"jailbreak\" markers in tool result content.',\n prefilter: ['jailbreak', 'jailbroken'],\n regex: /\\bjailbroken?\\s+(?:mode|response|model|assistant)\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'tool-call-injection',\n description:\n 'Tool-call markers that try to coerce the model into invoking a tool from the result content.',\n prefilter: ['<tool_use', '<function_call', '<invoke'],\n regex: /<(?:tool_use|function_call|invoke|tool_call)\\b[^>]*>/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'role-tag-injection',\n description:\n 'Chat-role tags injected into tool results to spoof a system / assistant message in the conversation.',\n prefilter: ['<|im_start|', '<|system|', '<|assistant|', '<|user|'],\n regex: /<\\|(?:im_start|system|assistant|user|im_end)\\|>/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n];\n\n/**\n * The default-on imperative-pattern catalogue. Stable across patches;\n * additions during the pre-1.0 window are minor-bumps because new\n * patterns may produce additional `tool.inbound.sanitization.hit{...}`\n * counter increments on existing deployments.\n *\n * @stable\n */\nexport const BUILT_IN_IMPERATIVE_PATTERNS: readonly ImperativePattern[] = PATTERNS;\n\n/**\n * Combined Aho-Corasick-style prefilter set across every pattern.\n * Lower-cased substrings; consumers test the body once with the\n * combined filter before iterating regexes.\n *\n * @stable\n */\nexport const IMPERATIVE_PREFILTER_SUBSTRINGS: ReadonlyArray<string> = Object.freeze([\n ...new Set(BUILT_IN_IMPERATIVE_PATTERNS.flatMap((p) => p.prefilter.map((s) => s.toLowerCase()))),\n]);\n\n/**\n * Compiled scan helper. Returns the list of pattern names that fired\n * AND the number of bytes the strip would remove if applied. Bounded\n * by the budget hint — when exceeded, returns `null` to let the caller\n * apply the best-effort `'detect-failed'` annotation.\n *\n * @stable\n */\nexport interface ScanResult {\n readonly hits: ReadonlyArray<{ readonly pattern: string; readonly matchCount: number }>;\n readonly bytesMatched: number;\n readonly scanDurationUs: number;\n}\n\n/**\n * Run the imperative-pattern scan against `body`. Patterns are\n * iterated in catalogue order; the prefilter shortcut returns early\n * for bodies that do not contain any imperative-family substring.\n *\n * @stable\n */\nexport function scanImperativePatterns(\n body: string,\n patterns: ReadonlyArray<ImperativePattern> = BUILT_IN_IMPERATIVE_PATTERNS,\n budgetMs = 5,\n): ScanResult | null {\n const start = performance.now();\n if (body.length === 0) {\n return { hits: [], bytesMatched: 0, scanDurationUs: 0 };\n }\n const lower = body.toLowerCase();\n let prefilterHit = false;\n for (const sub of IMPERATIVE_PREFILTER_SUBSTRINGS) {\n if (lower.includes(sub)) {\n prefilterHit = true;\n break;\n }\n }\n if (!prefilterHit) {\n return {\n hits: [],\n bytesMatched: 0,\n scanDurationUs: Math.round((performance.now() - start) * 1000),\n };\n }\n const hits: { pattern: string; matchCount: number }[] = [];\n let bytesMatched = 0;\n for (const pattern of patterns) {\n if (performance.now() - start > budgetMs) return null;\n const localPrefilterMatch = pattern.prefilter.some((sub) => lower.includes(sub.toLowerCase()));\n if (!localPrefilterMatch) continue;\n let matchCount = 0;\n pattern.regex.lastIndex = 0;\n let match: RegExpExecArray | null = pattern.regex.exec(body);\n while (match !== null) {\n matchCount++;\n bytesMatched += match[0].length;\n if (pattern.regex.lastIndex === match.index) {\n pattern.regex.lastIndex = match.index + 1;\n }\n match = pattern.regex.exec(body);\n }\n if (matchCount > 0) {\n hits.push({ pattern: pattern.name as string, matchCount });\n }\n }\n return {\n hits,\n bytesMatched,\n scanDurationUs: Math.round((performance.now() - start) * 1000),\n };\n}\n\n/**\n * Apply `pattern.mask` to every match of every pattern in `body`. Used\n * by the `'detect-and-strip*'` policies. The mask is calibrated to NOT\n * match any imperative pattern itself, so post-strip bodies do not\n * trigger another scan hit on round trips.\n *\n * @stable\n */\nexport function stripImperativePatterns(\n body: string,\n patterns: ReadonlyArray<ImperativePattern> = BUILT_IN_IMPERATIVE_PATTERNS,\n): string {\n let out = body;\n for (const pattern of patterns) {\n out = out.replace(pattern.regex, pattern.mask);\n }\n return out;\n}\n"],"mappings":";AAkEA,MAAMA,WAAyC;CAC7C;EACE,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAU;GAAa;GAAS;EAC5C,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAU;GAAS;GAAQ;EACvC,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAY;GAAoB;GAAwB;GAAU;EAC9E,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAiB;GAAkB;GAAU;GAAS;GAAO;EACzE,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAe;GAAqB;GAAW;GAAS;EACpE,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAkB;GAAc;GAAe;EAC3D,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aAAa;EACb,WAAW,CAAC,aAAa,aAAa;EACtC,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAa;GAAkB;GAAU;EACrD,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAe;GAAa;GAAgB;GAAU;EAClE,OAAO;EACP,MAAM;EACP;CACF;;;;;;;;;AAUD,MAAaC,+BAA6D;;;;;;;;AAS1E,MAAaC,kCAAyD,OAAO,OAAO,CAClF,GAAG,IAAI,IAAI,6BAA6B,SAAS,MAAM,EAAE,UAAU,KAAK,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CACjG,CAAC;;;;;;;;AAuBF,SAAgB,uBACd,MACA,WAA6C,8BAC7C,WAAW,GACQ;CACnB,MAAM,QAAQ,YAAY,KAAK;AAC/B,KAAI,KAAK,WAAW,EAClB,QAAO;EAAE,MAAM,EAAE;EAAE,cAAc;EAAG,gBAAgB;EAAG;CAEzD,MAAM,QAAQ,KAAK,aAAa;CAChC,IAAI,eAAe;AACnB,MAAK,MAAM,OAAO,gCAChB,KAAI,MAAM,SAAS,IAAI,EAAE;AACvB,iBAAe;AACf;;AAGJ,KAAI,CAAC,aACH,QAAO;EACL,MAAM,EAAE;EACR,cAAc;EACd,gBAAgB,KAAK,OAAO,YAAY,KAAK,GAAG,SAAS,IAAK;EAC/D;CAEH,MAAMC,OAAkD,EAAE;CAC1D,IAAI,eAAe;AACnB,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,YAAY,KAAK,GAAG,QAAQ,SAAU,QAAO;AAEjD,MAAI,CADwB,QAAQ,UAAU,MAAM,QAAQ,MAAM,SAAS,IAAI,aAAa,CAAC,CAAC,CACpE;EAC1B,IAAI,aAAa;AACjB,UAAQ,MAAM,YAAY;EAC1B,IAAIC,QAAgC,QAAQ,MAAM,KAAK,KAAK;AAC5D,SAAO,UAAU,MAAM;AACrB;AACA,mBAAgB,MAAM,GAAG;AACzB,OAAI,QAAQ,MAAM,cAAc,MAAM,MACpC,SAAQ,MAAM,YAAY,MAAM,QAAQ;AAE1C,WAAQ,QAAQ,MAAM,KAAK,KAAK;;AAElC,MAAI,aAAa,EACf,MAAK,KAAK;GAAE,SAAS,QAAQ;GAAgB;GAAY,CAAC;;AAG9D,QAAO;EACL;EACA;EACA,gBAAgB,KAAK,OAAO,YAAY,KAAK,GAAG,SAAS,IAAK;EAC/D;;;;;;;;;;AAWH,SAAgB,wBACd,MACA,WAA6C,8BACrC;CACR,IAAI,MAAM;AACV,MAAK,MAAM,WAAW,SACpB,OAAM,IAAI,QAAQ,QAAQ,OAAO,QAAQ,KAAK;AAEhD,QAAO"}
|
|
1
|
+
{"version":3,"file":"imperative-patterns.js","names":["PATTERNS: readonly ImperativePattern[]","BUILT_IN_IMPERATIVE_PATTERNS: readonly ImperativePattern[]","IMPERATIVE_PREFILTER_SUBSTRINGS: ReadonlyArray<string>","hits: { pattern: string; matchCount: number }[]","match: RegExpExecArray | null"],"sources":["../../src/redaction/imperative-patterns.ts"],"sourcesContent":["/**\n * Imperative-pattern catalogue for inbound prompt-injection defence.\n *\n * Sibling to {@link BUILT_IN_PATTERNS} (PII / secrets) - the two\n * catalogues are disjoint by construction. The imperative catalogue\n * is consumed by the inbound sanitization layer in `@graphorin/tools`\n * to scan tool / MCP results before they reach the agent's message\n * store; the PII / secrets catalogue is consumed by the outbound\n * exporter validators to scan span attributes before they reach an\n * exporter.\n *\n * The patterns target the canonical English \"ignore previous\n * instructions\" / \"system override\" injection family - the concrete\n * surface that an untrusted-skill or MCP server result might use to\n * smuggle imperative content into the next provider call. The\n * catalogue is intentionally conservative: every entry has a fixed-\n * substring prefilter so the per-byte scan budget stays sub-millisecond\n * on typical 16 KB tool results.\n *\n * @packageDocumentation\n */\n\n/**\n * Stable name of an imperative pattern. The catalogue is curated;\n * user-supplied patterns can use any identifier they want and will be\n * passed through the sanitization layer alongside the built-ins.\n *\n * @stable\n */\nexport type ImperativePatternName =\n | 'ignore-previous-instructions'\n | 'forget-instructions'\n | 'override-instructions'\n | 'system-prompt-leak'\n | 'role-reassignment'\n | 'developer-mode'\n | 'jailbreak-marker'\n | 'tool-call-injection'\n | 'role-tag-injection';\n\n/**\n * One entry in the imperative-pattern catalogue. The shape mirrors\n * {@link BUILT_IN_PATTERNS} so consumers can share scan / replace\n * machinery, but the fields are typed as imperative-only so the two\n * catalogues do not accidentally merge.\n *\n * @stable\n */\nexport interface ImperativePattern {\n readonly name: ImperativePatternName | (string & {});\n readonly description: string;\n /**\n * Cheap substring prefilter applied before the regex; if the body\n * does not contain any of the prefilter substrings the regex is\n * skipped entirely. The prefilter is case-insensitive.\n */\n readonly prefilter: ReadonlyArray<string>;\n /**\n * Full regex applied when the prefilter matches. Always carries the\n * `g` and `i` flags; the catalogue construction validates this.\n */\n readonly regex: RegExp;\n /** Replacement string applied by the `'detect-and-strip*'` policies. */\n readonly mask: string;\n}\n\nconst PATTERNS: readonly ImperativePattern[] = [\n {\n name: 'ignore-previous-instructions',\n description:\n 'The canonical \"ignore previous instructions\" injection - the most common form of prompt injection seen across MCP results.',\n prefilter: ['ignore', 'disregard', 'forget'],\n regex:\n /\\b(?:ignore|disregard|forget)\\s+(?:all\\s+)?(?:the\\s+)?(?:previous|prior|above|earlier|preceding|original)\\s+(?:instructions?|prompts?|messages?|directives?|rules?)\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'forget-instructions',\n description: 'Imperative \"forget what I told you\" / memory-wipe family.',\n prefilter: ['forget', 'erase', 'clear'],\n regex:\n /\\b(?:forget|erase|clear|wipe)\\s+(?:everything|all)\\s+(?:above|before|previous|i\\s+(?:told|said))\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'override-instructions',\n description: '\"Override the system prompt\" / \"new instructions\" injection family.',\n prefilter: ['override', 'new instructions', 'updated instructions', 'replace'],\n regex:\n /\\b(?:override|replace)\\s+(?:the\\s+)?(?:system\\s+)?(?:prompt|instructions?|directives?)\\b|\\bnew\\s+(?:instructions?|prompts?|directives?)\\s*(?::|follow|are)\\b|\\bupdated\\s+(?:instructions?|prompts?|directives?)\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'system-prompt-leak',\n description:\n 'Requests asking the model to reveal its system prompt / hidden instructions / configuration.',\n prefilter: ['system prompt', 'system message', 'reveal', 'print', 'show'],\n regex:\n /\\b(?:reveal|print|show|output|repeat|expose|disclose)\\s+(?:your|the)\\s+(?:system\\s+(?:prompt|message|instructions?)|hidden\\s+instructions?|initial\\s+(?:prompt|instructions?)|configuration)\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'role-reassignment',\n description: '\"You are now ...\" / role-reassignment imperative family.',\n prefilter: ['you are now', 'you are no longer', 'pretend', 'act as'],\n regex:\n /\\byou\\s+are\\s+(?:now|no\\s+longer)\\s+(?:a\\b|an\\b|the\\b)|\\bpretend\\s+(?:to\\s+be|you\\s+are)\\b|\\bact\\s+as\\s+(?:a|an|the|if\\s+you)\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'developer-mode',\n description:\n 'Requests to enter \"developer mode\" / \"DAN\" / unrestricted modes - a long-running jailbreak family.',\n prefilter: ['developer mode', 'admin mode', 'unrestricted'],\n regex:\n /\\b(?:enter|enable|activate)\\s+(?:developer|admin|debug|unrestricted|god|root)\\s+mode\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'jailbreak-marker',\n description: 'Direct \"jailbreak\" markers in tool result content.',\n prefilter: ['jailbreak', 'jailbroken'],\n regex: /\\bjailbroken?\\s+(?:mode|response|model|assistant)\\b/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'tool-call-injection',\n description:\n 'Tool-call markers that try to coerce the model into invoking a tool from the result content.',\n prefilter: ['<tool_use', '<function_call', '<invoke'],\n regex: /<(?:tool_use|function_call|invoke|tool_call)\\b[^>]*>/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n {\n name: 'role-tag-injection',\n description:\n 'Chat-role tags injected into tool results to spoof a system / assistant message in the conversation.',\n prefilter: ['<|im_start|', '<|system|', '<|assistant|', '<|user|'],\n regex: /<\\|(?:im_start|system|assistant|user|im_end)\\|>/gi,\n mask: '[REDACTED:imperative-pattern]',\n },\n];\n\n/**\n * The default-on imperative-pattern catalogue. Stable across patches;\n * additions during the pre-1.0 window are minor-bumps because new\n * patterns may produce additional `tool.inbound.sanitization.hit{...}`\n * counter increments on existing deployments.\n *\n * @stable\n */\nexport const BUILT_IN_IMPERATIVE_PATTERNS: readonly ImperativePattern[] = PATTERNS;\n\n/**\n * Combined Aho-Corasick-style prefilter set across every pattern.\n * Lower-cased substrings; consumers test the body once with the\n * combined filter before iterating regexes.\n *\n * @stable\n */\nexport const IMPERATIVE_PREFILTER_SUBSTRINGS: ReadonlyArray<string> = Object.freeze([\n ...new Set(BUILT_IN_IMPERATIVE_PATTERNS.flatMap((p) => p.prefilter.map((s) => s.toLowerCase()))),\n]);\n\n/**\n * Compiled scan helper. Returns the list of pattern names that fired\n * AND the number of bytes the strip would remove if applied. Bounded\n * by the budget hint - when exceeded, returns `null` to let the caller\n * apply the best-effort `'detect-failed'` annotation.\n *\n * @stable\n */\nexport interface ScanResult {\n readonly hits: ReadonlyArray<{ readonly pattern: string; readonly matchCount: number }>;\n readonly bytesMatched: number;\n readonly scanDurationUs: number;\n}\n\n/**\n * Run the imperative-pattern scan against `body`. Patterns are\n * iterated in catalogue order; the prefilter shortcut returns early\n * for bodies that do not contain any imperative-family substring.\n *\n * @stable\n */\nexport function scanImperativePatterns(\n body: string,\n patterns: ReadonlyArray<ImperativePattern> = BUILT_IN_IMPERATIVE_PATTERNS,\n budgetMs = 5,\n): ScanResult | null {\n const start = performance.now();\n if (body.length === 0) {\n return { hits: [], bytesMatched: 0, scanDurationUs: 0 };\n }\n const lower = body.toLowerCase();\n let prefilterHit = false;\n for (const sub of IMPERATIVE_PREFILTER_SUBSTRINGS) {\n if (lower.includes(sub)) {\n prefilterHit = true;\n break;\n }\n }\n if (!prefilterHit) {\n return {\n hits: [],\n bytesMatched: 0,\n scanDurationUs: Math.round((performance.now() - start) * 1000),\n };\n }\n const hits: { pattern: string; matchCount: number }[] = [];\n let bytesMatched = 0;\n for (const pattern of patterns) {\n if (performance.now() - start > budgetMs) return null;\n const localPrefilterMatch = pattern.prefilter.some((sub) => lower.includes(sub.toLowerCase()));\n if (!localPrefilterMatch) continue;\n let matchCount = 0;\n pattern.regex.lastIndex = 0;\n let match: RegExpExecArray | null = pattern.regex.exec(body);\n while (match !== null) {\n matchCount++;\n bytesMatched += match[0].length;\n if (pattern.regex.lastIndex === match.index) {\n pattern.regex.lastIndex = match.index + 1;\n }\n match = pattern.regex.exec(body);\n }\n if (matchCount > 0) {\n hits.push({ pattern: pattern.name as string, matchCount });\n }\n }\n return {\n hits,\n bytesMatched,\n scanDurationUs: Math.round((performance.now() - start) * 1000),\n };\n}\n\n/**\n * Apply `pattern.mask` to every match of every pattern in `body`. Used\n * by the `'detect-and-strip*'` policies. The mask is calibrated to NOT\n * match any imperative pattern itself, so post-strip bodies do not\n * trigger another scan hit on round trips.\n *\n * @stable\n */\nexport function stripImperativePatterns(\n body: string,\n patterns: ReadonlyArray<ImperativePattern> = BUILT_IN_IMPERATIVE_PATTERNS,\n): string {\n let out = body;\n for (const pattern of patterns) {\n out = out.replace(pattern.regex, pattern.mask);\n }\n return out;\n}\n"],"mappings":";AAkEA,MAAMA,WAAyC;CAC7C;EACE,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAU;GAAa;GAAS;EAC5C,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAU;GAAS;GAAQ;EACvC,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAY;GAAoB;GAAwB;GAAU;EAC9E,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAiB;GAAkB;GAAU;GAAS;GAAO;EACzE,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aAAa;EACb,WAAW;GAAC;GAAe;GAAqB;GAAW;GAAS;EACpE,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAkB;GAAc;GAAe;EAC3D,OACE;EACF,MAAM;EACP;CACD;EACE,MAAM;EACN,aAAa;EACb,WAAW,CAAC,aAAa,aAAa;EACtC,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAa;GAAkB;GAAU;EACrD,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,aACE;EACF,WAAW;GAAC;GAAe;GAAa;GAAgB;GAAU;EAClE,OAAO;EACP,MAAM;EACP;CACF;;;;;;;;;AAUD,MAAaC,+BAA6D;;;;;;;;AAS1E,MAAaC,kCAAyD,OAAO,OAAO,CAClF,GAAG,IAAI,IAAI,6BAA6B,SAAS,MAAM,EAAE,UAAU,KAAK,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CACjG,CAAC;;;;;;;;AAuBF,SAAgB,uBACd,MACA,WAA6C,8BAC7C,WAAW,GACQ;CACnB,MAAM,QAAQ,YAAY,KAAK;AAC/B,KAAI,KAAK,WAAW,EAClB,QAAO;EAAE,MAAM,EAAE;EAAE,cAAc;EAAG,gBAAgB;EAAG;CAEzD,MAAM,QAAQ,KAAK,aAAa;CAChC,IAAI,eAAe;AACnB,MAAK,MAAM,OAAO,gCAChB,KAAI,MAAM,SAAS,IAAI,EAAE;AACvB,iBAAe;AACf;;AAGJ,KAAI,CAAC,aACH,QAAO;EACL,MAAM,EAAE;EACR,cAAc;EACd,gBAAgB,KAAK,OAAO,YAAY,KAAK,GAAG,SAAS,IAAK;EAC/D;CAEH,MAAMC,OAAkD,EAAE;CAC1D,IAAI,eAAe;AACnB,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,YAAY,KAAK,GAAG,QAAQ,SAAU,QAAO;AAEjD,MAAI,CADwB,QAAQ,UAAU,MAAM,QAAQ,MAAM,SAAS,IAAI,aAAa,CAAC,CAAC,CACpE;EAC1B,IAAI,aAAa;AACjB,UAAQ,MAAM,YAAY;EAC1B,IAAIC,QAAgC,QAAQ,MAAM,KAAK,KAAK;AAC5D,SAAO,UAAU,MAAM;AACrB;AACA,mBAAgB,MAAM,GAAG;AACzB,OAAI,QAAQ,MAAM,cAAc,MAAM,MACpC,SAAQ,MAAM,YAAY,MAAM,QAAQ;AAE1C,WAAQ,QAAQ,MAAM,KAAK,KAAK;;AAElC,MAAI,aAAa,EACf,MAAK,KAAK;GAAE,SAAS,QAAQ;GAAgB;GAAY,CAAC;;AAG9D,QAAO;EACL;EACA;EACA,gBAAgB,KAAK,OAAO,YAAY,KAAK,GAAG,SAAS,IAAK;EAC/D;;;;;;;;;;AAWH,SAAgB,wBACd,MACA,WAA6C,8BACrC;CACR,IAAI,MAAM;AACV,MAAK,MAAM,WAAW,SACpB,OAAM,IAAI,QAAQ,QAAQ,OAAO,QAAQ,KAAK;AAEhD,QAAO"}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
//#region src/redaction/patterns.d.ts
|
|
2
2
|
/**
|
|
3
3
|
* Built-in PII / secret detection patterns. The catalogue is intentionally
|
|
4
|
-
* conservative
|
|
4
|
+
* conservative - every pattern has both positive and negative test
|
|
5
5
|
* fixtures and is documented so operators understand exactly what is
|
|
6
6
|
* matched.
|
|
7
7
|
*
|
|
8
8
|
* The catalogue is split into two groups:
|
|
9
9
|
*
|
|
10
|
-
* - **secret**
|
|
10
|
+
* - **secret** - credentials, API tokens, JWTs, private keys. Matches
|
|
11
11
|
* are always dropped + counted, regardless of the configured tier
|
|
12
12
|
* floor.
|
|
13
|
-
* - **pii**
|
|
13
|
+
* - **pii** - email / phone / IBAN / credit card / SSN / IP address.
|
|
14
14
|
* Subject to the configured tier floor + per-pattern enable / disable
|
|
15
15
|
* knobs.
|
|
16
16
|
*
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
type BuiltInPatternName = 'graphorin-token' | 'openai-key' | 'anthropic-key' | 'aws-access-key' | 'gcp-service-account' | 'github-pat' | 'jwt' | 'bearer-header' | 'basic-auth' | 'private-key-pem' | 'email' | 'creditcard' | 'us-ssn' | 'phone-e164' | 'iban' | 'ipv4' | 'ipv6';
|
|
27
27
|
/**
|
|
28
|
-
* Pattern category
|
|
28
|
+
* Pattern category - `secret` matches always force a drop; `pii`
|
|
29
29
|
* matches respect the configured `enabledPatterns` allow-list.
|
|
30
30
|
*
|
|
31
31
|
* @stable
|
|
@@ -45,7 +45,7 @@ interface RedactionPattern {
|
|
|
45
45
|
readonly mask?: string;
|
|
46
46
|
/**
|
|
47
47
|
* Optional per-match predicate (RP-21). When present, a regex hit is only
|
|
48
|
-
* treated as a real match
|
|
48
|
+
* treated as a real match - and masked - when this returns `true` for the
|
|
49
49
|
* matched substring. Used by the `creditcard` pattern to require a valid
|
|
50
50
|
* Luhn checksum so look-alike digit runs (epoch-ms timestamps, order ids)
|
|
51
51
|
* are not corrupted.
|
|
@@ -74,7 +74,7 @@ declare const BUILT_IN_PATTERNS: readonly RedactionPattern[];
|
|
|
74
74
|
*/
|
|
75
75
|
declare const OPT_IN_PATTERNS: readonly RedactionPattern[];
|
|
76
76
|
/**
|
|
77
|
-
* Full registry
|
|
77
|
+
* Full registry - for tooling that wants to introspect every pattern
|
|
78
78
|
* the framework knows about (e.g. CLI `graphorin redaction list`).
|
|
79
79
|
*
|
|
80
80
|
* @stable
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns.d.ts","names":[],"sources":["../../src/redaction/patterns.ts"],"sourcesContent":[],"mappings":";;AAyBA;AAyBA;AAOA;
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","names":[],"sources":["../../src/redaction/patterns.ts"],"sourcesContent":[],"mappings":";;AAyBA;AAyBA;AAOA;AAmKA;AAUA;AAUA;;;;;;;;;;;;;;;;;;KAvNY,kBAAA;;;;;;;KAyBA,eAAA;;;;;;UAOK,gBAAA;;qBAEI;;kBAEH;;;;;;;;;;;;;;;;;;;;;;;;;cA+JL,4BAA4B;;;;;;;cAU5B,0BAA0B;;;;;;;cAU1B,gCAAgC"}
|
|
@@ -3,8 +3,8 @@ const PATTERNS = [
|
|
|
3
3
|
{
|
|
4
4
|
name: "graphorin-token",
|
|
5
5
|
category: "secret",
|
|
6
|
-
description: "Graphorin server token (`
|
|
7
|
-
regex: /
|
|
6
|
+
description: "Graphorin server token (`gph_<env>_v1_<entropy>_<crc32>`; default prefix).",
|
|
7
|
+
regex: /gph_[a-z0-9]{2,12}_v1_[A-Za-z0-9]{20,80}_[A-Za-z0-9]{6}/g,
|
|
8
8
|
mask: "[REDACTED graphorin-token]"
|
|
9
9
|
},
|
|
10
10
|
{
|
|
@@ -81,7 +81,7 @@ const PATTERNS = [
|
|
|
81
81
|
{
|
|
82
82
|
name: "creditcard",
|
|
83
83
|
category: "pii",
|
|
84
|
-
description: "Credit card number (13
|
|
84
|
+
description: "Credit card number (13-19 digits, optional spaces / dashes; Luhn-checked).",
|
|
85
85
|
regex: /\b(?:\d[\s-]*?){13,19}\b/g,
|
|
86
86
|
mask: "[REDACTED creditcard]",
|
|
87
87
|
verify: isLuhnValid
|
|
@@ -139,7 +139,7 @@ const BUILT_IN_PATTERNS = PATTERNS.filter((p) => p.optIn !== true);
|
|
|
139
139
|
*/
|
|
140
140
|
const OPT_IN_PATTERNS = PATTERNS.filter((p) => p.optIn === true);
|
|
141
141
|
/**
|
|
142
|
-
* Full registry
|
|
142
|
+
* Full registry - for tooling that wants to introspect every pattern
|
|
143
143
|
* the framework knows about (e.g. CLI `graphorin redaction list`).
|
|
144
144
|
*
|
|
145
145
|
* @stable
|
|
@@ -147,7 +147,7 @@ const OPT_IN_PATTERNS = PATTERNS.filter((p) => p.optIn === true);
|
|
|
147
147
|
const ALL_BUILT_IN_PATTERNS = PATTERNS;
|
|
148
148
|
/**
|
|
149
149
|
* Luhn (mod-10) checksum validator used by the `creditcard` pattern (RP-21).
|
|
150
|
-
* Strips spaces / dashes, bounds the length to 13
|
|
150
|
+
* Strips spaces / dashes, bounds the length to 13-19 digits, and verifies the
|
|
151
151
|
* checksum so a digit run that merely *looks* like a PAN is not redacted.
|
|
152
152
|
*/
|
|
153
153
|
function isLuhnValid(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns.js","names":["PATTERNS: readonly RedactionPattern[]","BUILT_IN_PATTERNS: readonly RedactionPattern[]","OPT_IN_PATTERNS: readonly RedactionPattern[]","ALL_BUILT_IN_PATTERNS: readonly RedactionPattern[]"],"sources":["../../src/redaction/patterns.ts"],"sourcesContent":["/**\n * Built-in PII / secret detection patterns. The catalogue is intentionally\n * conservative — every pattern has both positive and negative test\n * fixtures and is documented so operators understand exactly what is\n * matched.\n *\n * The catalogue is split into two groups:\n *\n * - **secret** — credentials, API tokens, JWTs, private keys. Matches\n * are always dropped + counted, regardless of the configured tier\n * floor.\n * - **pii** — email / phone / IBAN / credit card / SSN / IP address.\n * Subject to the configured tier floor + per-pattern enable / disable\n * knobs.\n *\n * @packageDocumentation\n */\n\n/**\n * Stable pattern identifier. The catalogue is curated; user-supplied\n * patterns can use any identifier they want and will be passed through\n * the validator in addition to the built-ins.\n *\n * @stable\n */\nexport type BuiltInPatternName =\n | 'graphorin-token'\n | 'openai-key'\n | 'anthropic-key'\n | 'aws-access-key'\n | 'gcp-service-account'\n | 'github-pat'\n | 'jwt'\n | 'bearer-header'\n | 'basic-auth'\n | 'private-key-pem'\n | 'email'\n | 'creditcard'\n | 'us-ssn'\n | 'phone-e164'\n | 'iban'\n | 'ipv4'\n | 'ipv6';\n\n/**\n * Pattern category — `secret` matches always force a drop; `pii`\n * matches respect the configured `enabledPatterns` allow-list.\n *\n * @stable\n */\nexport type PatternCategory = 'secret' | 'pii';\n\n/**\n * One entry in the redaction catalogue.\n *\n * @stable\n */\nexport interface RedactionPattern {\n readonly name: string;\n readonly category: PatternCategory;\n readonly description: string;\n readonly regex: RegExp;\n /** Replacement string used when `mode === 'mask'`. */\n readonly mask?: string;\n /**\n * Optional per-match predicate (RP-21). When present, a regex hit is only\n * treated as a real match — and masked — when this returns `true` for the\n * matched substring. Used by the `creditcard` pattern to require a valid\n * Luhn checksum so look-alike digit runs (epoch-ms timestamps, order ids)\n * are not corrupted.\n */\n readonly verify?: (match: string) => boolean;\n /**\n * Optional opt-in flag. When `true` the pattern is **not** active by\n * default; operators must add it to `enabledPatterns` explicitly. Used\n * by the IPv4 / IPv6 patterns because raw IPs frequently appear in\n * non-PII log lines (host headers, debug traces, …).\n */\n readonly optIn?: boolean;\n}\n\nconst PATTERNS: readonly RedactionPattern[] = [\n {\n name: 'graphorin-token',\n category: 'secret',\n description: 'Graphorin server token (`kru_<env>_v1_<entropy>_<crc32>`).',\n regex: /kru_(?:dev|test|prod)_v1_[A-Za-z0-9]{20,80}_[A-Za-z0-9]{6}/g,\n mask: '[REDACTED graphorin-token]',\n },\n {\n name: 'openai-key',\n category: 'secret',\n description: 'OpenAI API key (`sk-...`).',\n regex: /\\bsk-[A-Za-z0-9_-]{20,}\\b/g,\n mask: '[REDACTED openai-key]',\n },\n {\n name: 'anthropic-key',\n category: 'secret',\n description: 'Anthropic API key (`sk-ant-...`).',\n regex: /\\bsk-ant-[A-Za-z0-9_-]{20,}\\b/g,\n mask: '[REDACTED anthropic-key]',\n },\n {\n name: 'aws-access-key',\n category: 'secret',\n description: 'AWS access-key id (`AKIA...`).',\n regex: /\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/g,\n mask: '[REDACTED aws-access-key]',\n },\n {\n name: 'gcp-service-account',\n category: 'secret',\n description:\n 'GCP service-account email (`...@...iam.gserviceaccount.com`). Opt-in; the `email` pattern already redacts the address shape; this entry exists for operators wanting the explicit `gcp-service-account` counter label.',\n regex: /[A-Za-z0-9._-]+@[A-Za-z0-9-]+\\.iam\\.gserviceaccount\\.com\\b/g,\n mask: '[REDACTED gcp-service-account]',\n optIn: true,\n },\n {\n name: 'github-pat',\n category: 'secret',\n description: 'GitHub personal-access token / OAuth token / app token.',\n regex: /\\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,255}\\b/g,\n mask: '[REDACTED github-pat]',\n },\n {\n name: 'jwt',\n category: 'secret',\n description: 'JSON Web Token (`eyJ...`).',\n regex: /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g,\n mask: '[REDACTED jwt]',\n },\n {\n name: 'bearer-header',\n category: 'secret',\n description: '`Authorization: Bearer ...` header values.',\n regex: /\\bBearer\\s+[A-Za-z0-9._-]{16,}\\b/gi,\n mask: 'Bearer [REDACTED]',\n },\n {\n name: 'basic-auth',\n category: 'secret',\n description: '`Authorization: Basic ...` header values.',\n regex: /\\bBasic\\s+[A-Za-z0-9+/=]{16,}\\b/gi,\n mask: 'Basic [REDACTED]',\n },\n {\n name: 'private-key-pem',\n category: 'secret',\n description: 'PEM private-key block (`-----BEGIN ... PRIVATE KEY-----`).',\n regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,\n mask: '[REDACTED private-key-pem]',\n },\n {\n name: 'email',\n category: 'pii',\n description: 'RFC-5322-ish email address.',\n regex: /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g,\n mask: '[REDACTED email]',\n },\n {\n name: 'creditcard',\n category: 'pii',\n description: 'Credit card number (13–19 digits, optional spaces / dashes; Luhn-checked).',\n regex: /\\b(?:\\d[\\s-]*?){13,19}\\b/g,\n mask: '[REDACTED creditcard]',\n // RP-21: require a valid Luhn checksum so a 13–19 digit run that is not a\n // real PAN (millisecond epoch timestamps, order numbers, …) is left alone.\n verify: isLuhnValid,\n },\n {\n name: 'us-ssn',\n category: 'pii',\n description: 'US Social Security Number (`123-45-6789`).',\n regex: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\n mask: '[REDACTED us-ssn]',\n },\n {\n name: 'phone-e164',\n category: 'pii',\n description: 'E.164 phone number (`+11234567890`).',\n regex: /\\+\\d{7,15}\\b/g,\n mask: '[REDACTED phone-e164]',\n },\n {\n name: 'iban',\n category: 'pii',\n description: 'IBAN (2-letter country + 2 check digits + up to 30 alphanumerics).',\n regex: /\\b[A-Z]{2}\\d{2}[A-Z0-9]{11,30}\\b/g,\n mask: '[REDACTED iban]',\n },\n {\n name: 'ipv4',\n category: 'pii',\n description: 'IPv4 dotted-quad address. Opt-in (often appears legitimately in logs).',\n regex: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g,\n mask: '[REDACTED ipv4]',\n optIn: true,\n },\n {\n name: 'ipv6',\n category: 'pii',\n description: 'IPv6 address (full / compressed). Opt-in.',\n regex: /\\b(?:[0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}\\b/g,\n mask: '[REDACTED ipv6]',\n optIn: true,\n },\n];\n\n/**\n * The 14 default-on built-in patterns (the IPv4 and IPv6 detectors are\n * opt-in and live in {@link OPT_IN_PATTERNS}).\n *\n * @stable\n */\nexport const BUILT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS.filter(\n (p) => p.optIn !== true,\n);\n\n/**\n * Patterns that are recognised by the validator but are NOT enabled by\n * default. Use them via `patterns: [...BUILT_IN_PATTERNS, ...OPT_IN_PATTERNS]`.\n *\n * @stable\n */\nexport const OPT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS.filter(\n (p) => p.optIn === true,\n);\n\n/**\n * Full registry — for tooling that wants to introspect every pattern\n * the framework knows about (e.g. CLI `graphorin redaction list`).\n *\n * @stable\n */\nexport const ALL_BUILT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS;\n\n/**\n * Luhn (mod-10) checksum validator used by the `creditcard` pattern (RP-21).\n * Strips spaces / dashes, bounds the length to 13–19 digits, and verifies the\n * checksum so a digit run that merely *looks* like a PAN is not redacted.\n */\nfunction isLuhnValid(value: string): boolean {\n const digits = value.replace(/\\D/g, '');\n if (digits.length < 13 || digits.length > 19) return false;\n let sum = 0;\n let double = false;\n for (let i = digits.length - 1; i >= 0; i -= 1) {\n let d = digits.charCodeAt(i) - 48; // '0'\n if (double) {\n d *= 2;\n if (d > 9) d -= 9;\n }\n sum += d;\n double = !double;\n }\n return sum % 10 === 0;\n}\n"],"mappings":";AAiFA,MAAMA,WAAwC;CAC5C;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aACE;EACF,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EAGN,QAAQ;EACT;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACF;;;;;;;AAQD,MAAaC,oBAAiD,SAAS,QACpE,MAAM,EAAE,UAAU,KACpB;;;;;;;AAQD,MAAaC,kBAA+C,SAAS,QAClE,MAAM,EAAE,UAAU,KACpB;;;;;;;AAQD,MAAaC,wBAAqD;;;;;;AAOlE,SAAS,YAAY,OAAwB;CAC3C,MAAM,SAAS,MAAM,QAAQ,OAAO,GAAG;AACvC,KAAI,OAAO,SAAS,MAAM,OAAO,SAAS,GAAI,QAAO;CACrD,IAAI,MAAM;CACV,IAAI,SAAS;AACb,MAAK,IAAI,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;EAC9C,IAAI,IAAI,OAAO,WAAW,EAAE,GAAG;AAC/B,MAAI,QAAQ;AACV,QAAK;AACL,OAAI,IAAI,EAAG,MAAK;;AAElB,SAAO;AACP,WAAS,CAAC;;AAEZ,QAAO,MAAM,OAAO"}
|
|
1
|
+
{"version":3,"file":"patterns.js","names":["PATTERNS: readonly RedactionPattern[]","BUILT_IN_PATTERNS: readonly RedactionPattern[]","OPT_IN_PATTERNS: readonly RedactionPattern[]","ALL_BUILT_IN_PATTERNS: readonly RedactionPattern[]"],"sources":["../../src/redaction/patterns.ts"],"sourcesContent":["/**\n * Built-in PII / secret detection patterns. The catalogue is intentionally\n * conservative - every pattern has both positive and negative test\n * fixtures and is documented so operators understand exactly what is\n * matched.\n *\n * The catalogue is split into two groups:\n *\n * - **secret** - credentials, API tokens, JWTs, private keys. Matches\n * are always dropped + counted, regardless of the configured tier\n * floor.\n * - **pii** - email / phone / IBAN / credit card / SSN / IP address.\n * Subject to the configured tier floor + per-pattern enable / disable\n * knobs.\n *\n * @packageDocumentation\n */\n\n/**\n * Stable pattern identifier. The catalogue is curated; user-supplied\n * patterns can use any identifier they want and will be passed through\n * the validator in addition to the built-ins.\n *\n * @stable\n */\nexport type BuiltInPatternName =\n | 'graphorin-token'\n | 'openai-key'\n | 'anthropic-key'\n | 'aws-access-key'\n | 'gcp-service-account'\n | 'github-pat'\n | 'jwt'\n | 'bearer-header'\n | 'basic-auth'\n | 'private-key-pem'\n | 'email'\n | 'creditcard'\n | 'us-ssn'\n | 'phone-e164'\n | 'iban'\n | 'ipv4'\n | 'ipv6';\n\n/**\n * Pattern category - `secret` matches always force a drop; `pii`\n * matches respect the configured `enabledPatterns` allow-list.\n *\n * @stable\n */\nexport type PatternCategory = 'secret' | 'pii';\n\n/**\n * One entry in the redaction catalogue.\n *\n * @stable\n */\nexport interface RedactionPattern {\n readonly name: string;\n readonly category: PatternCategory;\n readonly description: string;\n readonly regex: RegExp;\n /** Replacement string used when `mode === 'mask'`. */\n readonly mask?: string;\n /**\n * Optional per-match predicate (RP-21). When present, a regex hit is only\n * treated as a real match - and masked - when this returns `true` for the\n * matched substring. Used by the `creditcard` pattern to require a valid\n * Luhn checksum so look-alike digit runs (epoch-ms timestamps, order ids)\n * are not corrupted.\n */\n readonly verify?: (match: string) => boolean;\n /**\n * Optional opt-in flag. When `true` the pattern is **not** active by\n * default; operators must add it to `enabledPatterns` explicitly. Used\n * by the IPv4 / IPv6 patterns because raw IPs frequently appear in\n * non-PII log lines (host headers, debug traces, …).\n */\n readonly optIn?: boolean;\n}\n\nconst PATTERNS: readonly RedactionPattern[] = [\n {\n name: 'graphorin-token',\n category: 'secret',\n // Matches the DEFAULT token prefix from @graphorin/security\n // (DEFAULT_TOKEN_PREFIX = 'gph'); deployments that configure a\n // custom prefix must register their own pattern. The env label is\n // matched loosely because `acceptEnvironments` is operator-extensible.\n description: 'Graphorin server token (`gph_<env>_v1_<entropy>_<crc32>`; default prefix).',\n regex: /gph_[a-z0-9]{2,12}_v1_[A-Za-z0-9]{20,80}_[A-Za-z0-9]{6}/g,\n mask: '[REDACTED graphorin-token]',\n },\n {\n name: 'openai-key',\n category: 'secret',\n description: 'OpenAI API key (`sk-...`).',\n regex: /\\bsk-[A-Za-z0-9_-]{20,}\\b/g,\n mask: '[REDACTED openai-key]',\n },\n {\n name: 'anthropic-key',\n category: 'secret',\n description: 'Anthropic API key (`sk-ant-...`).',\n regex: /\\bsk-ant-[A-Za-z0-9_-]{20,}\\b/g,\n mask: '[REDACTED anthropic-key]',\n },\n {\n name: 'aws-access-key',\n category: 'secret',\n description: 'AWS access-key id (`AKIA...`).',\n regex: /\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/g,\n mask: '[REDACTED aws-access-key]',\n },\n {\n name: 'gcp-service-account',\n category: 'secret',\n description:\n 'GCP service-account email (`...@...iam.gserviceaccount.com`). Opt-in; the `email` pattern already redacts the address shape; this entry exists for operators wanting the explicit `gcp-service-account` counter label.',\n regex: /[A-Za-z0-9._-]+@[A-Za-z0-9-]+\\.iam\\.gserviceaccount\\.com\\b/g,\n mask: '[REDACTED gcp-service-account]',\n optIn: true,\n },\n {\n name: 'github-pat',\n category: 'secret',\n description: 'GitHub personal-access token / OAuth token / app token.',\n regex: /\\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,255}\\b/g,\n mask: '[REDACTED github-pat]',\n },\n {\n name: 'jwt',\n category: 'secret',\n description: 'JSON Web Token (`eyJ...`).',\n regex: /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g,\n mask: '[REDACTED jwt]',\n },\n {\n name: 'bearer-header',\n category: 'secret',\n description: '`Authorization: Bearer ...` header values.',\n regex: /\\bBearer\\s+[A-Za-z0-9._-]{16,}\\b/gi,\n mask: 'Bearer [REDACTED]',\n },\n {\n name: 'basic-auth',\n category: 'secret',\n description: '`Authorization: Basic ...` header values.',\n regex: /\\bBasic\\s+[A-Za-z0-9+/=]{16,}\\b/gi,\n mask: 'Basic [REDACTED]',\n },\n {\n name: 'private-key-pem',\n category: 'secret',\n description: 'PEM private-key block (`-----BEGIN ... PRIVATE KEY-----`).',\n regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,\n mask: '[REDACTED private-key-pem]',\n },\n {\n name: 'email',\n category: 'pii',\n description: 'RFC-5322-ish email address.',\n regex: /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g,\n mask: '[REDACTED email]',\n },\n {\n name: 'creditcard',\n category: 'pii',\n description: 'Credit card number (13-19 digits, optional spaces / dashes; Luhn-checked).',\n regex: /\\b(?:\\d[\\s-]*?){13,19}\\b/g,\n mask: '[REDACTED creditcard]',\n // RP-21: require a valid Luhn checksum so a 13-19 digit run that is not a\n // real PAN (millisecond epoch timestamps, order numbers, …) is left alone.\n verify: isLuhnValid,\n },\n {\n name: 'us-ssn',\n category: 'pii',\n description: 'US Social Security Number (`123-45-6789`).',\n regex: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\n mask: '[REDACTED us-ssn]',\n },\n {\n name: 'phone-e164',\n category: 'pii',\n description: 'E.164 phone number (`+11234567890`).',\n regex: /\\+\\d{7,15}\\b/g,\n mask: '[REDACTED phone-e164]',\n },\n {\n name: 'iban',\n category: 'pii',\n description: 'IBAN (2-letter country + 2 check digits + up to 30 alphanumerics).',\n regex: /\\b[A-Z]{2}\\d{2}[A-Z0-9]{11,30}\\b/g,\n mask: '[REDACTED iban]',\n },\n {\n name: 'ipv4',\n category: 'pii',\n description: 'IPv4 dotted-quad address. Opt-in (often appears legitimately in logs).',\n regex: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g,\n mask: '[REDACTED ipv4]',\n optIn: true,\n },\n {\n name: 'ipv6',\n category: 'pii',\n description: 'IPv6 address (full / compressed). Opt-in.',\n regex: /\\b(?:[0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}\\b/g,\n mask: '[REDACTED ipv6]',\n optIn: true,\n },\n];\n\n/**\n * The 14 default-on built-in patterns (the IPv4 and IPv6 detectors are\n * opt-in and live in {@link OPT_IN_PATTERNS}).\n *\n * @stable\n */\nexport const BUILT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS.filter(\n (p) => p.optIn !== true,\n);\n\n/**\n * Patterns that are recognised by the validator but are NOT enabled by\n * default. Use them via `patterns: [...BUILT_IN_PATTERNS, ...OPT_IN_PATTERNS]`.\n *\n * @stable\n */\nexport const OPT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS.filter(\n (p) => p.optIn === true,\n);\n\n/**\n * Full registry - for tooling that wants to introspect every pattern\n * the framework knows about (e.g. CLI `graphorin redaction list`).\n *\n * @stable\n */\nexport const ALL_BUILT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS;\n\n/**\n * Luhn (mod-10) checksum validator used by the `creditcard` pattern (RP-21).\n * Strips spaces / dashes, bounds the length to 13-19 digits, and verifies the\n * checksum so a digit run that merely *looks* like a PAN is not redacted.\n */\nfunction isLuhnValid(value: string): boolean {\n const digits = value.replace(/\\D/g, '');\n if (digits.length < 13 || digits.length > 19) return false;\n let sum = 0;\n let double = false;\n for (let i = digits.length - 1; i >= 0; i -= 1) {\n let d = digits.charCodeAt(i) - 48; // '0'\n if (double) {\n d *= 2;\n if (d > 9) d -= 9;\n }\n sum += d;\n double = !double;\n }\n return sum % 10 === 0;\n}\n"],"mappings":";AAiFA,MAAMA,WAAwC;CAC5C;EACE,MAAM;EACN,UAAU;EAKV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aACE;EACF,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EAGN,QAAQ;EACT;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACF;;;;;;;AAQD,MAAaC,oBAAiD,SAAS,QACpE,MAAM,EAAE,UAAU,KACpB;;;;;;;AAQD,MAAaC,kBAA+C,SAAS,QAClE,MAAM,EAAE,UAAU,KACpB;;;;;;;AAQD,MAAaC,wBAAqD;;;;;;AAOlE,SAAS,YAAY,OAAwB;CAC3C,MAAM,SAAS,MAAM,QAAQ,OAAO,GAAG;AACvC,KAAI,OAAO,SAAS,MAAM,OAAO,SAAS,GAAI,QAAO;CACrD,IAAI,MAAM;CACV,IAAI,SAAS;AACb,MAAK,IAAI,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;EAC9C,IAAI,IAAI,OAAO,WAAW,EAAE,GAAG;AAC/B,MAAI,QAAQ;AACV,QAAK;AACL,OAAI,IAAI,EAAG,MAAK;;AAElB,SAAO;AACP,WAAS,CAAC;;AAEZ,QAAO,MAAM,OAAO"}
|
|
@@ -6,7 +6,7 @@ import { RedactionInput, RedactionInput as RedactionInput$1, RedactionOutput, Re
|
|
|
6
6
|
/**
|
|
7
7
|
* Optional sink invoked every time the validator drops a value or
|
|
8
8
|
* masks a pattern. Useful for emitting custom metrics, audit entries,
|
|
9
|
-
* or alert hooks. The callback receives only sanitized data
|
|
9
|
+
* or alert hooks. The callback receives only sanitized data - secret
|
|
10
10
|
* values are never forwarded.
|
|
11
11
|
*
|
|
12
12
|
* @stable
|
|
@@ -4,7 +4,7 @@ import { SENSITIVITY_ORDER } from "@graphorin/core";
|
|
|
4
4
|
|
|
5
5
|
//#region src/redaction/validator.ts
|
|
6
6
|
/**
|
|
7
|
-
* `createRedactionValidator(...)`
|
|
7
|
+
* `createRedactionValidator(...)` - the building block for the
|
|
8
8
|
* mandatory `withValidation()` wrapper applied to every exporter.
|
|
9
9
|
*
|
|
10
10
|
* @packageDocumentation
|
|
@@ -132,7 +132,7 @@ function createRedactionValidator(opts = {}) {
|
|
|
132
132
|
}
|
|
133
133
|
function dropOrThrow(_input, violation) {
|
|
134
134
|
recordViolation(violation);
|
|
135
|
-
if (failOnUnredacted) throw new RedactionValidationError(`RedactionValidator dropped value (${violation.reason})
|
|
135
|
+
if (failOnUnredacted) throw new RedactionValidationError(`RedactionValidator dropped value (${violation.reason}) - set failOnUnredactedSensitive: false in production to keep the pipeline running.`, violation);
|
|
136
136
|
return null;
|
|
137
137
|
}
|
|
138
138
|
return {
|