@openauditmodel/cli 0.1.0

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.
Files changed (134) hide show
  1. package/LICENSE +215 -0
  2. package/README.md +647 -0
  3. package/dist/conformance/src/cli.d.ts +20 -0
  4. package/dist/conformance/src/cli.js +560 -0
  5. package/dist/conformance/src/cli.js.map +1 -0
  6. package/dist/conformance/src/format-errors.d.ts +20 -0
  7. package/dist/conformance/src/format-errors.js +116 -0
  8. package/dist/conformance/src/format-errors.js.map +1 -0
  9. package/dist/conformance/src/integrity/canonicalize.d.ts +25 -0
  10. package/dist/conformance/src/integrity/canonicalize.js +100 -0
  11. package/dist/conformance/src/integrity/canonicalize.js.map +1 -0
  12. package/dist/conformance/src/integrity/digest.d.ts +54 -0
  13. package/dist/conformance/src/integrity/digest.js +126 -0
  14. package/dist/conformance/src/integrity/digest.js.map +1 -0
  15. package/dist/conformance/src/integrity/types.d.ts +86 -0
  16. package/dist/conformance/src/integrity/types.js +36 -0
  17. package/dist/conformance/src/integrity/types.js.map +1 -0
  18. package/dist/conformance/src/integrity/verify-chain.d.ts +23 -0
  19. package/dist/conformance/src/integrity/verify-chain.js +235 -0
  20. package/dist/conformance/src/integrity/verify-chain.js.map +1 -0
  21. package/dist/conformance/src/integrity/verify-event.d.ts +29 -0
  22. package/dist/conformance/src/integrity/verify-event.js +140 -0
  23. package/dist/conformance/src/integrity/verify-event.js.map +1 -0
  24. package/dist/conformance/src/privacy/entropy.d.ts +14 -0
  25. package/dist/conformance/src/privacy/entropy.js +86 -0
  26. package/dist/conformance/src/privacy/entropy.js.map +1 -0
  27. package/dist/conformance/src/privacy/field-names.d.ts +36 -0
  28. package/dist/conformance/src/privacy/field-names.js +121 -0
  29. package/dist/conformance/src/privacy/field-names.js.map +1 -0
  30. package/dist/conformance/src/privacy/lint-event.d.ts +18 -0
  31. package/dist/conformance/src/privacy/lint-event.js +272 -0
  32. package/dist/conformance/src/privacy/lint-event.js.map +1 -0
  33. package/dist/conformance/src/privacy/rules.d.ts +44 -0
  34. package/dist/conformance/src/privacy/rules.js +161 -0
  35. package/dist/conformance/src/privacy/rules.js.map +1 -0
  36. package/dist/conformance/src/privacy/safe-formats.d.ts +32 -0
  37. package/dist/conformance/src/privacy/safe-formats.js +118 -0
  38. package/dist/conformance/src/privacy/safe-formats.js.map +1 -0
  39. package/dist/conformance/src/privacy/size-analysis.d.ts +36 -0
  40. package/dist/conformance/src/privacy/size-analysis.js +87 -0
  41. package/dist/conformance/src/privacy/size-analysis.js.map +1 -0
  42. package/dist/conformance/src/privacy/token-patterns.d.ts +24 -0
  43. package/dist/conformance/src/privacy/token-patterns.js +126 -0
  44. package/dist/conformance/src/privacy/token-patterns.js.map +1 -0
  45. package/dist/conformance/src/privacy/traverse.d.ts +33 -0
  46. package/dist/conformance/src/privacy/traverse.js +61 -0
  47. package/dist/conformance/src/privacy/traverse.js.map +1 -0
  48. package/dist/conformance/src/privacy/types.d.ts +58 -0
  49. package/dist/conformance/src/privacy/types.js +32 -0
  50. package/dist/conformance/src/privacy/types.js.map +1 -0
  51. package/dist/conformance/src/privacy/url-analysis.d.ts +40 -0
  52. package/dist/conformance/src/privacy/url-analysis.js +148 -0
  53. package/dist/conformance/src/privacy/url-analysis.js.map +1 -0
  54. package/dist/conformance/src/profiles/check-profile.d.ts +22 -0
  55. package/dist/conformance/src/profiles/check-profile.js +72 -0
  56. package/dist/conformance/src/profiles/check-profile.js.map +1 -0
  57. package/dist/conformance/src/profiles/evaluate-rule.d.ts +27 -0
  58. package/dist/conformance/src/profiles/evaluate-rule.js +87 -0
  59. package/dist/conformance/src/profiles/evaluate-rule.js.map +1 -0
  60. package/dist/conformance/src/profiles/load-profile.d.ts +30 -0
  61. package/dist/conformance/src/profiles/load-profile.js +95 -0
  62. package/dist/conformance/src/profiles/load-profile.js.map +1 -0
  63. package/dist/conformance/src/profiles/resolve-pointer.d.ts +44 -0
  64. package/dist/conformance/src/profiles/resolve-pointer.js +123 -0
  65. package/dist/conformance/src/profiles/resolve-pointer.js.map +1 -0
  66. package/dist/conformance/src/profiles/select-rules.d.ts +13 -0
  67. package/dist/conformance/src/profiles/select-rules.js +42 -0
  68. package/dist/conformance/src/profiles/select-rules.js.map +1 -0
  69. package/dist/conformance/src/profiles/types.d.ts +101 -0
  70. package/dist/conformance/src/profiles/types.js +31 -0
  71. package/dist/conformance/src/profiles/types.js.map +1 -0
  72. package/dist/conformance/src/profiles/validate-profile-definition.d.ts +12 -0
  73. package/dist/conformance/src/profiles/validate-profile-definition.js +47 -0
  74. package/dist/conformance/src/profiles/validate-profile-definition.js.map +1 -0
  75. package/dist/conformance/src/sources.d.ts +53 -0
  76. package/dist/conformance/src/sources.js +153 -0
  77. package/dist/conformance/src/sources.js.map +1 -0
  78. package/dist/conformance/src/validate-core.d.ts +30 -0
  79. package/dist/conformance/src/validate-core.js +62 -0
  80. package/dist/conformance/src/validate-core.js.map +1 -0
  81. package/dist/conformance/src/validate.d.ts +34 -0
  82. package/dist/conformance/src/validate.js +62 -0
  83. package/dist/conformance/src/validate.js.map +1 -0
  84. package/dist/conformance/src/validator-interface.d.ts +35 -0
  85. package/dist/conformance/src/validator-interface.js +19 -0
  86. package/dist/conformance/src/validator-interface.js.map +1 -0
  87. package/package.json +85 -0
  88. package/profiles/README.md +201 -0
  89. package/profiles/api-and-integration-management/README.md +271 -0
  90. package/profiles/api-and-integration-management/profile.json +258 -0
  91. package/profiles/backup-and-recovery/README.md +318 -0
  92. package/profiles/backup-and-recovery/profile.json +178 -0
  93. package/profiles/customer-and-account-management/README.md +340 -0
  94. package/profiles/customer-and-account-management/profile.json +237 -0
  95. package/profiles/deployment-and-change-management/README.md +359 -0
  96. package/profiles/deployment-and-change-management/profile.json +298 -0
  97. package/profiles/document-management/README.md +120 -0
  98. package/profiles/document-management/profile.json +170 -0
  99. package/profiles/financial-transaction-management/README.md +339 -0
  100. package/profiles/financial-transaction-management/profile.json +247 -0
  101. package/profiles/identity-and-access-management/README.md +112 -0
  102. package/profiles/identity-and-access-management/profile.json +120 -0
  103. package/profiles/incident-management/README.md +338 -0
  104. package/profiles/incident-management/profile.json +256 -0
  105. package/profiles/message-broker-management/README.md +344 -0
  106. package/profiles/message-broker-management/profile.json +399 -0
  107. package/profiles/profile-definition.schema.json +213 -0
  108. package/profiles/secrets-and-key-management/README.md +331 -0
  109. package/profiles/secrets-and-key-management/profile.json +219 -0
  110. package/schemas/v0.1/audit-event.schema.json +911 -0
  111. package/semantic-conventions/README.md +52 -0
  112. package/semantic-conventions/authentication.md +111 -0
  113. package/semantic-conventions/configuration-and-change.md +122 -0
  114. package/semantic-conventions/correlation-and-tracing.md +387 -0
  115. package/semantic-conventions/data-access.md +115 -0
  116. package/semantic-conventions/event-naming.md +114 -0
  117. package/semantic-conventions/identity-and-access.md +112 -0
  118. package/semantic-conventions/privileged-operations.md +110 -0
  119. package/semantic-conventions/workflow-and-approval.md +109 -0
  120. package/specification/actor-model.md +135 -0
  121. package/specification/approval-and-delegation.md +150 -0
  122. package/specification/authentication.md +116 -0
  123. package/specification/authorization.md +117 -0
  124. package/specification/change-model.md +134 -0
  125. package/specification/delivery.md +136 -0
  126. package/specification/design-principles.md +137 -0
  127. package/specification/event-model.md +387 -0
  128. package/specification/evidence-model.md +109 -0
  129. package/specification/extension-model.md +159 -0
  130. package/specification/integrity.md +314 -0
  131. package/specification/overview.md +172 -0
  132. package/specification/privacy.md +386 -0
  133. package/specification/resource-model.md +116 -0
  134. package/specification/terminology.md +135 -0
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Recognizers for values that must not be reported by the entropy heuristic.
3
+ *
4
+ * Audit events are full of long, random-looking strings that are supposed to be
5
+ * there: event identifiers, trace context, integrity digests. Reporting them
6
+ * would bury the findings that matter.
7
+ *
8
+ * These exclusions apply **only** to the generic entropy rule. A value under a
9
+ * property named `password` is reported whatever it looks like, and a value
10
+ * matching a known token format is reported even if it also looks like an
11
+ * identifier.
12
+ *
13
+ * Every pattern here is anchored with a bounded quantifier, so none can
14
+ * backtrack catastrophically.
15
+ */
16
+ export declare function isUuid(value: string): boolean;
17
+ export declare function isUlid(value: string): boolean;
18
+ export declare function isTraceId(value: string): boolean;
19
+ export declare function isSpanId(value: string): boolean;
20
+ export declare function isHexDigest(value: string): boolean;
21
+ export declare function isRfc3339Timestamp(value: string): boolean;
22
+ export declare function isNumericIdentifier(value: string): boolean;
23
+ export declare function isPlainIdentifier(value: string): boolean;
24
+ /** True when a value stands in for a secret that was removed. */
25
+ export declare function isRedactionPlaceholder(value: string): boolean;
26
+ /** True when a value looks like a URL or a scheme-qualified locator. */
27
+ export declare function looksLikeLocator(value: string): boolean;
28
+ /**
29
+ * True when a value has a recognised, non-secret shape. Consulted only by the
30
+ * entropy heuristic.
31
+ */
32
+ export declare function isKnownSafeFormat(value: string): boolean;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Recognizers for values that must not be reported by the entropy heuristic.
3
+ *
4
+ * Audit events are full of long, random-looking strings that are supposed to be
5
+ * there: event identifiers, trace context, integrity digests. Reporting them
6
+ * would bury the findings that matter.
7
+ *
8
+ * These exclusions apply **only** to the generic entropy rule. A value under a
9
+ * property named `password` is reported whatever it looks like, and a value
10
+ * matching a known token format is reported even if it also looks like an
11
+ * identifier.
12
+ *
13
+ * Every pattern here is anchored with a bounded quantifier, so none can
14
+ * backtrack catastrophically.
15
+ */
16
+ const UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
17
+ /** Crockford base32, excluding I, L, O and U. */
18
+ const ULID = /^[0-9ABCDEFGHJKMNPQRSTVWXYZ]{26}$/;
19
+ const TRACE_ID = /^[0-9a-f]{32}$/;
20
+ const SPAN_ID = /^[0-9a-f]{16}$/;
21
+ /** Lower-case hexadecimal digests of the lengths the integrity verifier supports. */
22
+ const HEX_DIGEST = /^(?:[0-9a-f]{64}|[0-9a-f]{96}|[0-9a-f]{128})$/;
23
+ const RFC3339 = /^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:[Zz]|[+-]\d{2}:\d{2})$/;
24
+ const NUMERIC_ID = /^\d{1,32}$/;
25
+ /**
26
+ * A lower-case separated identifier or reference path, such as
27
+ * `payments.settlement.completed`, `chain-platform-control-service` or
28
+ * `records/root-cause-analysis/rca-2026-0418`.
29
+ *
30
+ * Two constraints keep this from swallowing real tokens. At least one separator
31
+ * is required, so an unbroken run is never excluded. And upper case is not
32
+ * accepted: a mixed-case alphanumeric run is exactly what a token looks like, so
33
+ * matching one here would blind the entropy rule to the values it exists to
34
+ * find. The cost is that an all-lower-case token containing a separator goes
35
+ * unreported, which is an accepted false negative.
36
+ */
37
+ const PLAIN_IDENTIFIER = /^[a-z][a-z0-9]*(?:[./_-][a-z0-9]+){1,32}$/;
38
+ /**
39
+ * Values that stand in for a removed secret. Reporting these punishes the
40
+ * behaviour the specification asks for, so they are not treated as exposures.
41
+ * The list is deliberately short and matched exactly: a broad "looks masked"
42
+ * rule would hide real values.
43
+ */
44
+ const REDACTION_PLACEHOLDERS = new Set([
45
+ "[redacted]",
46
+ "<redacted>",
47
+ "redacted",
48
+ "[masked]",
49
+ "<masked>",
50
+ "masked",
51
+ "[removed]",
52
+ "<removed>",
53
+ "removed",
54
+ "[omitted]",
55
+ "<omitted>",
56
+ "omitted",
57
+ "[hidden]",
58
+ "n/a",
59
+ "null",
60
+ "none",
61
+ "-",
62
+ "--",
63
+ ]);
64
+ /** Any run of asterisks or bullet characters, such as `********`. */
65
+ const MASK_CHARACTERS = /^[*•·]{1,64}$/;
66
+ export function isUuid(value) {
67
+ return UUID.test(value);
68
+ }
69
+ export function isUlid(value) {
70
+ return ULID.test(value);
71
+ }
72
+ export function isTraceId(value) {
73
+ return TRACE_ID.test(value);
74
+ }
75
+ export function isSpanId(value) {
76
+ return SPAN_ID.test(value);
77
+ }
78
+ export function isHexDigest(value) {
79
+ return HEX_DIGEST.test(value);
80
+ }
81
+ export function isRfc3339Timestamp(value) {
82
+ return RFC3339.test(value);
83
+ }
84
+ export function isNumericIdentifier(value) {
85
+ return NUMERIC_ID.test(value);
86
+ }
87
+ export function isPlainIdentifier(value) {
88
+ return PLAIN_IDENTIFIER.test(value);
89
+ }
90
+ /** True when a value stands in for a secret that was removed. */
91
+ export function isRedactionPlaceholder(value) {
92
+ const trimmed = value.trim();
93
+ if (trimmed === "") {
94
+ return true;
95
+ }
96
+ return REDACTION_PLACEHOLDERS.has(trimmed.toLowerCase()) || MASK_CHARACTERS.test(trimmed);
97
+ }
98
+ /** True when a value looks like a URL or a scheme-qualified locator. */
99
+ export function looksLikeLocator(value) {
100
+ return value.includes("://");
101
+ }
102
+ /**
103
+ * True when a value has a recognised, non-secret shape. Consulted only by the
104
+ * entropy heuristic.
105
+ */
106
+ export function isKnownSafeFormat(value) {
107
+ return (isUuid(value) ||
108
+ isUlid(value) ||
109
+ isTraceId(value) ||
110
+ isSpanId(value) ||
111
+ isHexDigest(value) ||
112
+ isRfc3339Timestamp(value) ||
113
+ isNumericIdentifier(value) ||
114
+ isPlainIdentifier(value) ||
115
+ looksLikeLocator(value) ||
116
+ isRedactionPlaceholder(value));
117
+ }
118
+ //# sourceMappingURL=safe-formats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-formats.js","sourceRoot":"","sources":["../../../../conformance/src/privacy/safe-formats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Size analysis for the containers that are captured wholesale most often.
3
+ *
4
+ * This rule cannot tell a carefully chosen large object from a serialized
5
+ * database row. It reports that a value is large enough to suggest the second,
6
+ * and says so in those terms: the finding is about **minimization**, not about
7
+ * personal data, and it never claims to know what the value contains.
8
+ *
9
+ * Thresholds are fixed in v0.1 and documented in specification/privacy.md §6.
10
+ * They are deliberately generous; the aim is to catch dumping, not to police
11
+ * legitimately detailed metadata.
12
+ */
13
+ export interface SizeThresholds {
14
+ readonly serializedBytes: number;
15
+ readonly totalProperties: number;
16
+ readonly maxDepth: number;
17
+ readonly maxArrayLength: number;
18
+ readonly longestString: number;
19
+ }
20
+ export declare const SIZE_THRESHOLDS: SizeThresholds;
21
+ /** Depth at which profiling stops descending, so that a hostile document cannot exhaust the stack. */
22
+ export declare const MAX_PROFILE_DEPTH = 64;
23
+ export interface SizeProfile {
24
+ readonly serializedBytes: number;
25
+ readonly totalProperties: number;
26
+ readonly maxDepth: number;
27
+ readonly maxArrayLength: number;
28
+ readonly longestString: number;
29
+ }
30
+ /** Measures a value without inspecting what it means. */
31
+ export declare function profileValue(value: unknown): SizeProfile;
32
+ /**
33
+ * Returns the human-readable signals a profile exceeded. The strings describe
34
+ * measurements only; they never contain any part of the value.
35
+ */
36
+ export declare function exceededSignals(profile: SizeProfile, thresholds?: SizeThresholds): string[];
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Size analysis for the containers that are captured wholesale most often.
3
+ *
4
+ * This rule cannot tell a carefully chosen large object from a serialized
5
+ * database row. It reports that a value is large enough to suggest the second,
6
+ * and says so in those terms: the finding is about **minimization**, not about
7
+ * personal data, and it never claims to know what the value contains.
8
+ *
9
+ * Thresholds are fixed in v0.1 and documented in specification/privacy.md §6.
10
+ * They are deliberately generous; the aim is to catch dumping, not to police
11
+ * legitimately detailed metadata.
12
+ */
13
+ export const SIZE_THRESHOLDS = {
14
+ serializedBytes: 4096,
15
+ totalProperties: 50,
16
+ maxDepth: 6,
17
+ maxArrayLength: 100,
18
+ longestString: 2048,
19
+ };
20
+ /** Depth at which profiling stops descending, so that a hostile document cannot exhaust the stack. */
21
+ export const MAX_PROFILE_DEPTH = 64;
22
+ /** Measures a value without inspecting what it means. */
23
+ export function profileValue(value) {
24
+ let totalProperties = 0;
25
+ let maxDepth = 0;
26
+ let maxArrayLength = 0;
27
+ let longestString = 0;
28
+ const walk = (node, depth) => {
29
+ if (depth > maxDepth) {
30
+ maxDepth = depth;
31
+ }
32
+ if (depth >= MAX_PROFILE_DEPTH) {
33
+ return;
34
+ }
35
+ if (typeof node === "string") {
36
+ longestString = Math.max(longestString, node.length);
37
+ return;
38
+ }
39
+ if (Array.isArray(node)) {
40
+ maxArrayLength = Math.max(maxArrayLength, node.length);
41
+ for (const item of node) {
42
+ walk(item, depth + 1);
43
+ }
44
+ return;
45
+ }
46
+ if (node !== null && typeof node === "object") {
47
+ for (const [, member] of Object.entries(node)) {
48
+ totalProperties += 1;
49
+ walk(member, depth + 1);
50
+ }
51
+ }
52
+ };
53
+ walk(value, 0);
54
+ let serializedBytes;
55
+ try {
56
+ serializedBytes = Buffer.byteLength(JSON.stringify(value) ?? "", "utf8");
57
+ }
58
+ catch {
59
+ // A value that cannot be serialized cannot be measured; it is not reported.
60
+ serializedBytes = 0;
61
+ }
62
+ return { serializedBytes, totalProperties, maxDepth, maxArrayLength, longestString };
63
+ }
64
+ /**
65
+ * Returns the human-readable signals a profile exceeded. The strings describe
66
+ * measurements only; they never contain any part of the value.
67
+ */
68
+ export function exceededSignals(profile, thresholds = SIZE_THRESHOLDS) {
69
+ const signals = [];
70
+ if (profile.serializedBytes > thresholds.serializedBytes) {
71
+ signals.push(`serialized size ${profile.serializedBytes} bytes exceeds ${thresholds.serializedBytes}`);
72
+ }
73
+ if (profile.totalProperties > thresholds.totalProperties) {
74
+ signals.push(`${profile.totalProperties} properties exceeds ${thresholds.totalProperties}`);
75
+ }
76
+ if (profile.maxDepth > thresholds.maxDepth) {
77
+ signals.push(`nesting depth ${profile.maxDepth} exceeds ${thresholds.maxDepth}`);
78
+ }
79
+ if (profile.maxArrayLength > thresholds.maxArrayLength) {
80
+ signals.push(`array of ${profile.maxArrayLength} items exceeds ${thresholds.maxArrayLength}`);
81
+ }
82
+ if (profile.longestString > thresholds.longestString) {
83
+ signals.push(`string of ${profile.longestString} characters exceeds ${thresholds.longestString}`);
84
+ }
85
+ return signals;
86
+ }
87
+ //# sourceMappingURL=size-analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"size-analysis.js","sourceRoot":"","sources":["../../../../conformance/src/privacy/size-analysis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /** True when a value is shaped like an authorization header credential. */
2
+ export declare function matchesAuthorizationHeader(value: string): boolean;
3
+ /**
4
+ * True when a value contains a private key marker. Matching is a plain substring
5
+ * search, so there is no pattern to backtrack, and public certificates and
6
+ * public keys never match because their markers do not contain `PRIVATE KEY`.
7
+ */
8
+ export declare function containsPrivateKeyMaterial(value: string): boolean;
9
+ /**
10
+ * True when a value is structured as a JSON Web Token.
11
+ *
12
+ * Three dot-separated segments alone are not enough: arbitrary data splits that
13
+ * way too. The header and payload must decode from base64url to JSON **objects**
14
+ * and the header must declare an algorithm, which is what JWS requires.
15
+ *
16
+ * The signature is never checked, the decoded values are never returned, and no
17
+ * claim ever reaches a report.
18
+ */
19
+ export declare function isJwtStructured(value: string): boolean;
20
+ /**
21
+ * Returns the rule identifier for a published credential format, or
22
+ * `undefined`. The matched value is never returned.
23
+ */
24
+ export declare function matchKnownTokenFormat(value: string): string | undefined;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Recognizers for credential-shaped values.
3
+ *
4
+ * Every pattern is anchored, uses bounded quantifiers and contains no nested
5
+ * quantifier or back-reference, so none can backtrack catastrophically. No
6
+ * pattern uses look-behind, which keeps them portable across engines.
7
+ *
8
+ * A match identifies a value **shaped like** a credential. Nothing here checks
9
+ * whether a credential is real, current or usable, and no matched value is ever
10
+ * returned to a caller: the functions answer yes or no, so that a value cannot
11
+ * leak into a report by accident.
12
+ *
13
+ * The prefixes below are published by the systems that issue them. Recognising
14
+ * them is a property of this linter, not of the OpenAuditModel data model: the
15
+ * specification and the canonical schema remain vendor-neutral, and no rule here
16
+ * introduces a field, vocabulary or concept into the model.
17
+ */
18
+ import { RULES } from "./rules.js";
19
+ /**
20
+ * `Scheme <credential>`, as an HTTP authorization header value.
21
+ *
22
+ * The credential must be at least 16 characters of credential alphabet and the
23
+ * whole string must be exactly two whitespace-separated parts, so that ordinary
24
+ * prose beginning with one of these words is not matched.
25
+ */
26
+ const AUTHORIZATION_HEADER = /^(?:Bearer|Basic|Digest|ApiKey|Token)\s+[A-Za-z0-9._~+/=-]{16,4096}$/i;
27
+ /** PEM markers that introduce private key material. Public material is excluded. */
28
+ const PRIVATE_KEY_MARKERS = [
29
+ "BEGIN PRIVATE KEY",
30
+ "BEGIN RSA PRIVATE KEY",
31
+ "BEGIN DSA PRIVATE KEY",
32
+ "BEGIN EC PRIVATE KEY",
33
+ "BEGIN OPENSSH PRIVATE KEY",
34
+ "BEGIN PGP PRIVATE KEY BLOCK",
35
+ "BEGIN ENCRYPTED PRIVATE KEY",
36
+ ];
37
+ const BASE64URL_SEGMENT = /^[A-Za-z0-9_-]{4,8192}$/;
38
+ /** Published credential prefixes, each with the rule that reports it. */
39
+ const PREFIXED_TOKEN_PATTERNS = [
40
+ // Access key identifiers: a 4-character prefix and 16 upper-case characters.
41
+ { pattern: /^(?:AKIA|ASIA)[A-Z0-9]{16}$/, ruleId: RULES.AWS_ACCESS_KEY_ID.id },
42
+ // Personal, OAuth, user, server and refresh tokens, and fine-grained tokens.
43
+ { pattern: /^gh[pousr]_[A-Za-z0-9]{36,255}$/, ruleId: RULES.GITHUB_TOKEN.id },
44
+ { pattern: /^github_pat_[A-Za-z0-9_]{22,255}$/, ruleId: RULES.GITHUB_TOKEN.id },
45
+ { pattern: /^glpat-[A-Za-z0-9_-]{20,255}$/, ruleId: RULES.GITLAB_TOKEN.id },
46
+ { pattern: /^xox[abprs]-[A-Za-z0-9-]{10,255}$/, ruleId: RULES.SLACK_TOKEN.id },
47
+ { pattern: /^(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,255}$/, ruleId: RULES.PAYMENT_SECRET_KEY.id },
48
+ { pattern: /^AIza[A-Za-z0-9_-]{35}$/, ruleId: RULES.CLOUD_API_KEY.id },
49
+ ];
50
+ /** True when a value is shaped like an authorization header credential. */
51
+ export function matchesAuthorizationHeader(value) {
52
+ return AUTHORIZATION_HEADER.test(value.trim());
53
+ }
54
+ /**
55
+ * True when a value contains a private key marker. Matching is a plain substring
56
+ * search, so there is no pattern to backtrack, and public certificates and
57
+ * public keys never match because their markers do not contain `PRIVATE KEY`.
58
+ */
59
+ export function containsPrivateKeyMaterial(value) {
60
+ return PRIVATE_KEY_MARKERS.some((marker) => value.includes(marker));
61
+ }
62
+ /**
63
+ * True when a value is structured as a JSON Web Token.
64
+ *
65
+ * Three dot-separated segments alone are not enough: arbitrary data splits that
66
+ * way too. The header and payload must decode from base64url to JSON **objects**
67
+ * and the header must declare an algorithm, which is what JWS requires.
68
+ *
69
+ * The signature is never checked, the decoded values are never returned, and no
70
+ * claim ever reaches a report.
71
+ */
72
+ export function isJwtStructured(value) {
73
+ const segments = value.split(".");
74
+ if (segments.length !== 3) {
75
+ return false;
76
+ }
77
+ const [header, payload] = segments;
78
+ if (header === undefined || payload === undefined) {
79
+ return false;
80
+ }
81
+ if (!BASE64URL_SEGMENT.test(header) || !BASE64URL_SEGMENT.test(payload)) {
82
+ return false;
83
+ }
84
+ const decodedHeader = decodeJsonObject(header);
85
+ if (decodedHeader === undefined || typeof decodedHeader["alg"] !== "string") {
86
+ return false;
87
+ }
88
+ return decodeJsonObject(payload) !== undefined;
89
+ }
90
+ /** Decodes a base64url segment to a JSON object, or `undefined`. Never returns content to a report. */
91
+ function decodeJsonObject(segment) {
92
+ let text;
93
+ try {
94
+ text = Buffer.from(segment, "base64url").toString("utf8");
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ let parsed;
100
+ try {
101
+ parsed = JSON.parse(text);
102
+ }
103
+ catch {
104
+ return undefined;
105
+ }
106
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
107
+ return undefined;
108
+ }
109
+ return parsed;
110
+ }
111
+ /**
112
+ * Returns the rule identifier for a published credential format, or
113
+ * `undefined`. The matched value is never returned.
114
+ */
115
+ export function matchKnownTokenFormat(value) {
116
+ if (isJwtStructured(value)) {
117
+ return RULES.JWT_TOKEN.id;
118
+ }
119
+ for (const { pattern, ruleId } of PREFIXED_TOKEN_PATTERNS) {
120
+ if (pattern.test(value)) {
121
+ return ruleId;
122
+ }
123
+ }
124
+ return undefined;
125
+ }
126
+ //# sourceMappingURL=token-patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-patterns.js","sourceRoot":"","sources":["../../../../conformance/src/privacy/token-patterns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Deterministic JSON walker.
3
+ *
4
+ * Traversal uses own enumerable properties only, never reads a prototype and
5
+ * never invokes an accessor: input always comes from `JSON.parse`, but a caller
6
+ * could pass anything, and a linter that runs code contained in the thing it is
7
+ * inspecting is a vulnerability rather than a tool.
8
+ */
9
+ /** Depth at which traversal stops descending. */
10
+ export declare const MAX_LINT_DEPTH = 64;
11
+ /** One value encountered during traversal. */
12
+ export interface VisitedValue {
13
+ /** JSON Pointer to this value. */
14
+ readonly path: string;
15
+ /** Property name this value was found under, absent for array items and roots. */
16
+ readonly key?: string;
17
+ readonly value: unknown;
18
+ readonly depth: number;
19
+ }
20
+ /** Escapes a property name as a JSON Pointer reference token: `~` to `~0`, `/` to `~1`. */
21
+ export declare function pointerSegment(property: string): string;
22
+ /** Appends a property name or array index to a JSON Pointer. */
23
+ export declare function joinPointer(base: string, segment: string | number): string;
24
+ /**
25
+ * Visits a value and every value beneath it, in a stable order.
26
+ *
27
+ * The root is visited first, then each member in own-property order. Traversal
28
+ * stops at `MAX_LINT_DEPTH`; a document deep enough to reach it is reported by
29
+ * the size rule rather than silently truncated in the caller's mind.
30
+ */
31
+ export declare function traverse(root: unknown, basePath: string, visit: (visited: VisitedValue) => void): void;
32
+ /** Reads a nested value by property path, without traversing prototypes. */
33
+ export declare function readPath(root: unknown, properties: readonly string[]): unknown;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Deterministic JSON walker.
3
+ *
4
+ * Traversal uses own enumerable properties only, never reads a prototype and
5
+ * never invokes an accessor: input always comes from `JSON.parse`, but a caller
6
+ * could pass anything, and a linter that runs code contained in the thing it is
7
+ * inspecting is a vulnerability rather than a tool.
8
+ */
9
+ /** Depth at which traversal stops descending. */
10
+ export const MAX_LINT_DEPTH = 64;
11
+ /** Escapes a property name as a JSON Pointer reference token: `~` to `~0`, `/` to `~1`. */
12
+ export function pointerSegment(property) {
13
+ return property.replaceAll("~", "~0").replaceAll("/", "~1");
14
+ }
15
+ /** Appends a property name or array index to a JSON Pointer. */
16
+ export function joinPointer(base, segment) {
17
+ const encoded = typeof segment === "number" ? String(segment) : pointerSegment(segment);
18
+ return `${base}/${encoded}`;
19
+ }
20
+ /**
21
+ * Visits a value and every value beneath it, in a stable order.
22
+ *
23
+ * The root is visited first, then each member in own-property order. Traversal
24
+ * stops at `MAX_LINT_DEPTH`; a document deep enough to reach it is reported by
25
+ * the size rule rather than silently truncated in the caller's mind.
26
+ */
27
+ export function traverse(root, basePath, visit) {
28
+ const walk = (value, path, key, depth) => {
29
+ visit({ path, ...(key === undefined ? {} : { key }), value, depth });
30
+ if (depth >= MAX_LINT_DEPTH) {
31
+ return;
32
+ }
33
+ if (Array.isArray(value)) {
34
+ for (const [index, item] of value.entries()) {
35
+ walk(item, joinPointer(path, index), undefined, depth + 1);
36
+ }
37
+ return;
38
+ }
39
+ if (value !== null && typeof value === "object") {
40
+ for (const [property, member] of Object.entries(value)) {
41
+ walk(member, joinPointer(path, property), property, depth + 1);
42
+ }
43
+ }
44
+ };
45
+ walk(root, basePath, undefined, 0);
46
+ }
47
+ /** Reads a nested value by property path, without traversing prototypes. */
48
+ export function readPath(root, properties) {
49
+ let current = root;
50
+ for (const property of properties) {
51
+ if (current === null || typeof current !== "object" || Array.isArray(current)) {
52
+ return undefined;
53
+ }
54
+ if (!Object.hasOwn(current, property)) {
55
+ return undefined;
56
+ }
57
+ current = current[property];
58
+ }
59
+ return current;
60
+ }
61
+ //# sourceMappingURL=traverse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traverse.js","sourceRoot":"","sources":["../../../../conformance/src/privacy/traverse.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,iDAAiD;AACjD,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAYjC,2FAA2F;AAC3F,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,OAAO,QAAQ,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,OAAwB;IAChE,MAAM,OAAO,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IACxF,OAAO,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CACtB,IAAa,EACb,QAAgB,EAChB,KAAsC;IAEtC,MAAM,IAAI,GAAG,CAAC,KAAc,EAAE,IAAY,EAAE,GAAuB,EAAE,KAAa,EAAQ,EAAE;QAC1F,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAErE,IAAI,KAAK,IAAI,cAAc,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC5C,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YAC7D,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAChD,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;gBAClF,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,QAAQ,CAAC,IAAa,EAAE,UAA6B;IACnE,IAAI,OAAO,GAAY,IAAI,CAAC;IAE5B,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAClC,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9E,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC;YACtC,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,GAAI,OAAmC,CAAC,QAAQ,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Shared types for privacy linting.
3
+ *
4
+ * A **privacy finding** is a suspicion raised by deterministic static analysis.
5
+ * It is not proof of a data breach, a regulatory violation, a confirmed
6
+ * credential or confirmed personal data, and the absence of findings is not
7
+ * proof that an event is safe or compliant. See specification/privacy.md §6.
8
+ */
9
+ export type Severity = "info" | "low" | "medium" | "high" | "critical";
10
+ export type Confidence = "low" | "medium" | "high";
11
+ export declare const SEVERITY_ORDER: readonly Severity[];
12
+ /** One suspected privacy or secret-exposure risk. Never carries the offending value. */
13
+ export interface PrivacyFinding {
14
+ /** Stable rule identifier, such as `OAM-PRIV-001`. */
15
+ readonly ruleId: string;
16
+ /** How serious the risk would be if the suspicion is correct. */
17
+ readonly severity: Severity;
18
+ /** How confident the rule is that the suspicion is correct. */
19
+ readonly confidence: Confidence;
20
+ /** JSON Pointer to the offending location inside the event. */
21
+ readonly path: string;
22
+ /** One line describing what was observed. Never contains the observed value. */
23
+ readonly message: string;
24
+ /** Coarse grouping of the rule. */
25
+ readonly category?: string;
26
+ /** What to do about it. */
27
+ readonly recommendation?: string;
28
+ /** File the event was read from. Added by the command layer. */
29
+ readonly sourceFile?: string;
30
+ /** `id` of the event the finding belongs to. */
31
+ readonly eventId?: string;
32
+ }
33
+ export type LintStatus = "clean" | "findings" | "schema-invalid";
34
+ /** Outcome of linting one event. */
35
+ export interface EventLintResult {
36
+ readonly label: string;
37
+ readonly eventId?: string;
38
+ readonly status: LintStatus;
39
+ readonly findings: readonly PrivacyFinding[];
40
+ /**
41
+ * Rendered schema issues, present only when the event failed validation.
42
+ * Deep linting is not attempted for a malformed event, because traversing an
43
+ * arbitrary structure produces findings whose paths mean nothing.
44
+ */
45
+ readonly schemaIssues: readonly string[];
46
+ }
47
+ /** Counts for a run, for the summary line and the JSON report. */
48
+ export interface LintSummary {
49
+ readonly events: number;
50
+ readonly clean: number;
51
+ readonly withFindings: number;
52
+ readonly schemaInvalid: number;
53
+ readonly findings: number;
54
+ readonly bySeverity: Readonly<Record<Severity, number>>;
55
+ }
56
+ export declare function emptySeverityCounts(): Record<Severity, number>;
57
+ /** Summarises a set of results. */
58
+ export declare function summarise(results: readonly EventLintResult[]): LintSummary;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Shared types for privacy linting.
3
+ *
4
+ * A **privacy finding** is a suspicion raised by deterministic static analysis.
5
+ * It is not proof of a data breach, a regulatory violation, a confirmed
6
+ * credential or confirmed personal data, and the absence of findings is not
7
+ * proof that an event is safe or compliant. See specification/privacy.md §6.
8
+ */
9
+ export const SEVERITY_ORDER = ["critical", "high", "medium", "low", "info"];
10
+ export function emptySeverityCounts() {
11
+ return { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
12
+ }
13
+ /** Summarises a set of results. */
14
+ export function summarise(results) {
15
+ const bySeverity = emptySeverityCounts();
16
+ let findings = 0;
17
+ for (const result of results) {
18
+ for (const finding of result.findings) {
19
+ bySeverity[finding.severity] += 1;
20
+ findings += 1;
21
+ }
22
+ }
23
+ return {
24
+ events: results.length,
25
+ clean: results.filter((result) => result.status === "clean").length,
26
+ withFindings: results.filter((result) => result.status === "findings").length,
27
+ schemaInvalid: results.filter((result) => result.status === "schema-invalid").length,
28
+ findings,
29
+ bySeverity,
30
+ };
31
+ }
32
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../conformance/src/privacy/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,CAAC,MAAM,cAAc,GAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAkDjG,MAAM,UAAU,mBAAmB;IACjC,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,SAAS,CAAC,OAAmC;IAC3D,MAAM,UAAU,GAAG,mBAAmB,EAAE,CAAC;IACzC,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAClC,QAAQ,IAAI,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,MAAM;QACnE,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,MAAM;QAC7E,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,gBAAgB,CAAC,CAAC,MAAM;QACpF,QAAQ;QACR,UAAU;KACX,CAAC;AACJ,CAAC"}