@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,121 @@
1
+ /**
2
+ * Property-name registries.
3
+ *
4
+ * Matching is **exact after normalization**, never substring. Substring
5
+ * matching is what makes name-based linters unusable: `passwordPolicy`,
6
+ * `secretRotationEnabled`, `tokenCount`, `authorizationDecision` and
7
+ * `cookieConsent` are all legitimate audit fields that a substring rule would
8
+ * flag, and a rule that cries wolf is a rule that gets switched off.
9
+ *
10
+ * Normalization removes only case and the separators that the same concept is
11
+ * spelled with in different systems: `clientSecret`, `client_secret`,
12
+ * `client-secret` and `Client.Secret` are one name.
13
+ */
14
+ /** Lower-cases a property name and removes `-`, `_`, `.` and spaces. */
15
+ export function normalizeFieldName(name) {
16
+ return name.toLowerCase().replaceAll(/[-_. ]/g, "");
17
+ }
18
+ /**
19
+ * Property names whose value is a credential by definition. A field with one of
20
+ * these names has no non-sensitive interpretation.
21
+ */
22
+ export const PROHIBITED_CREDENTIAL_FIELD_NAMES = new Set([
23
+ // Passwords
24
+ "password",
25
+ "passwd",
26
+ "pwd",
27
+ "passphrase",
28
+ // Generic secrets
29
+ "secret",
30
+ "clientsecret",
31
+ "secretkey",
32
+ "credential",
33
+ "credentials",
34
+ // Tokens
35
+ "accesstoken",
36
+ "refreshtoken",
37
+ "idtoken",
38
+ "authtoken",
39
+ "bearertoken",
40
+ "sessiontoken",
41
+ // Keys
42
+ "apikey",
43
+ "privatekey",
44
+ "signingkey",
45
+ "encryptionkey",
46
+ // Transport credentials
47
+ "connectionstring",
48
+ "authorization",
49
+ "proxyauthorization",
50
+ "sessioncookie",
51
+ "cookie",
52
+ "setcookie",
53
+ ]);
54
+ /**
55
+ * Property names that carry whole request, response or message bodies. These
56
+ * are prohibited by specification/privacy.md §2 from being captured
57
+ * automatically, and a populated field with one of these names is evidence that
58
+ * they were.
59
+ */
60
+ export const RAW_PAYLOAD_FIELD_NAMES = new Set([
61
+ "requestbody",
62
+ "responsebody",
63
+ "rawrequest",
64
+ "rawresponse",
65
+ "httpbody",
66
+ "messagepayload",
67
+ "messagebody",
68
+ "brokerpayload",
69
+ "databaserow",
70
+ "fullrecord",
71
+ ]);
72
+ /**
73
+ * Names that are not credentials by themselves but that make a high-entropy
74
+ * value more suspicious. Used only to raise the confidence of the entropy rule
75
+ * from `low` to `medium`; never to raise a finding on its own.
76
+ */
77
+ export const SUSPICIOUS_FIELD_NAME_HINTS = new Set([
78
+ "token",
79
+ "key",
80
+ "secretvalue",
81
+ "credentialvalue",
82
+ "auth",
83
+ "authvalue",
84
+ "signature",
85
+ "nonce",
86
+ "salt",
87
+ "seed",
88
+ "apitoken",
89
+ "accesskey",
90
+ "secretaccesskey",
91
+ ]);
92
+ /**
93
+ * The names a property should be tested under.
94
+ *
95
+ * Extension keys are reverse-domain namespaced, so the field name is the final
96
+ * dot-separated segment: `com.example.audit.password` names a password, and
97
+ * normalizing the whole key would fold the namespace into it and hide that.
98
+ * Both forms are therefore tested. Because matching stays exact, a key ending in
99
+ * `.passwordPolicy` is still not a password.
100
+ */
101
+ function candidateNames(name) {
102
+ const candidates = [normalizeFieldName(name)];
103
+ const lastDot = name.lastIndexOf(".");
104
+ if (lastDot > 0 && lastDot < name.length - 1) {
105
+ candidates.push(normalizeFieldName(name.slice(lastDot + 1)));
106
+ }
107
+ return candidates;
108
+ }
109
+ function matchesAny(registry, name) {
110
+ return candidateNames(name).some((candidate) => registry.has(candidate));
111
+ }
112
+ export function isProhibitedCredentialFieldName(name) {
113
+ return matchesAny(PROHIBITED_CREDENTIAL_FIELD_NAMES, name);
114
+ }
115
+ export function isRawPayloadFieldName(name) {
116
+ return matchesAny(RAW_PAYLOAD_FIELD_NAMES, name);
117
+ }
118
+ export function isSuspiciousFieldName(name) {
119
+ return matchesAny(SUSPICIOUS_FIELD_NAME_HINTS, name);
120
+ }
121
+ //# sourceMappingURL=field-names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-names.js","sourceRoot":"","sources":["../../../../conformance/src/privacy/field-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,wEAAwE;AACxE,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAwB,IAAI,GAAG,CAAC;IAC5E,YAAY;IACZ,UAAU;IACV,QAAQ;IACR,KAAK;IACL,YAAY;IACZ,kBAAkB;IAClB,QAAQ;IACR,cAAc;IACd,WAAW;IACX,YAAY;IACZ,aAAa;IACb,SAAS;IACT,aAAa;IACb,cAAc;IACd,SAAS;IACT,WAAW;IACX,aAAa;IACb,cAAc;IACd,OAAO;IACP,QAAQ;IACR,YAAY;IACZ,YAAY;IACZ,eAAe;IACf,wBAAwB;IACxB,kBAAkB;IAClB,eAAe;IACf,oBAAoB;IACpB,eAAe;IACf,QAAQ;IACR,WAAW;CACZ,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAwB,IAAI,GAAG,CAAC;IAClE,aAAa;IACb,cAAc;IACd,YAAY;IACZ,aAAa;IACb,UAAU;IACV,gBAAgB;IAChB,aAAa;IACb,eAAe;IACf,aAAa;IACb,YAAY;CACb,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAwB,IAAI,GAAG,CAAC;IACtE,OAAO;IACP,KAAK;IACL,aAAa;IACb,iBAAiB;IACjB,MAAM;IACN,WAAW;IACX,WAAW;IACX,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,WAAW;IACX,iBAAiB;CAClB,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,SAAS,cAAc,CAAC,IAAY;IAClC,MAAM,UAAU,GAAG,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IAE9C,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7C,UAAU,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,UAAU,CAAC,QAA6B,EAAE,IAAY;IAC7D,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,+BAA+B,CAAC,IAAY;IAC1D,OAAO,UAAU,CAAC,iCAAiC,EAAE,IAAI,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,UAAU,CAAC,uBAAuB,EAAE,IAAI,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,UAAU,CAAC,2BAA2B,EAAE,IAAI,CAAC,CAAC;AACvD,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Privacy linting of a single audit event.
3
+ *
4
+ * The linter is deterministic, local and read-only. It resolves nothing, fetches
5
+ * nothing, opens no referenced file, sends nothing anywhere and never modifies
6
+ * or redacts an event. It reports suspicions about the values it was given.
7
+ *
8
+ * No finding ever carries the value that produced it. See
9
+ * specification/privacy.md §6 for what this can and cannot detect.
10
+ */
11
+ import type { EventValidator } from "../validate-core.js";
12
+ import { type EventLintResult } from "./types.js";
13
+ export interface LintOptions {
14
+ /** Validate against the canonical schema first. Defaults to true. */
15
+ readonly validateSchema?: boolean;
16
+ }
17
+ /** Lints one event and returns its findings, most severe first. */
18
+ export declare function lintEvent(event: unknown, label: string, validator: EventValidator, options?: LintOptions): EventLintResult;
@@ -0,0 +1,272 @@
1
+ import { isProhibitedCredentialFieldName, isRawPayloadFieldName, isSuspiciousFieldName, } from "./field-names.js";
2
+ import { isHighEntropyTokenCandidate } from "./entropy.js";
3
+ import { isRedactionPlaceholder } from "./safe-formats.js";
4
+ import { containsPrivateKeyMaterial, matchKnownTokenFormat, matchesAuthorizationHeader, } from "./token-patterns.js";
5
+ import { analyzeConnectionString, analyzeUrl, isDataSystemScheme } from "./url-analysis.js";
6
+ import { exceededSignals, profileValue } from "./size-analysis.js";
7
+ import { ALL_RULES, RULES } from "./rules.js";
8
+ import { readPath, traverse } from "./traverse.js";
9
+ import { SEVERITY_ORDER } from "./types.js";
10
+ /** Containers traversed in full, for both property names and values. */
11
+ function deepRoots(event) {
12
+ const roots = [];
13
+ const add = (path, value) => {
14
+ if (value !== undefined) {
15
+ roots.push({ path, value });
16
+ }
17
+ };
18
+ add("/metadata", readPath(event, ["metadata"]));
19
+ add("/extensions", readPath(event, ["extensions"]));
20
+ add("/actor/attributes", readPath(event, ["actor", "attributes"]));
21
+ add("/subject/attributes", readPath(event, ["subject", "attributes"]));
22
+ add("/resource/attributes", readPath(event, ["resource", "attributes"]));
23
+ add("/change/before", readPath(event, ["change", "before"]));
24
+ add("/change/after", readPath(event, ["change", "after"]));
25
+ const related = readPath(event, ["relatedResources"]);
26
+ if (Array.isArray(related)) {
27
+ for (const [index, entry] of related.entries()) {
28
+ add(`/relatedResources/${index}/attributes`, readPath(entry, ["attributes"]));
29
+ }
30
+ }
31
+ return roots;
32
+ }
33
+ /** Single values inspected for credential-shaped content, but not traversed. */
34
+ function scalarLocations(event) {
35
+ const locations = [];
36
+ const add = (path, value) => {
37
+ if (typeof value === "string") {
38
+ locations.push({ path, value });
39
+ }
40
+ };
41
+ add("/event/summary", readPath(event, ["event", "summary"]));
42
+ add("/event/error/message", readPath(event, ["event", "error", "message"]));
43
+ add("/reason/text", readPath(event, ["reason", "text"]));
44
+ add("/reason/reference", readPath(event, ["reason", "reference"]));
45
+ add("/request/route", readPath(event, ["request", "route"]));
46
+ add("/authorization/reason", readPath(event, ["authorization", "reason"]));
47
+ add("/delegation/reason", readPath(event, ["delegation", "reason"]));
48
+ return locations;
49
+ }
50
+ /** Containers measured by the minimization rule. */
51
+ function sizeRoots(event) {
52
+ return deepRoots(event).filter((root) => root.value !== null && typeof root.value === "object");
53
+ }
54
+ /** Evidence references, which have a rule of their own. */
55
+ function evidenceReferences(event) {
56
+ const evidence = readPath(event, ["evidence"]);
57
+ if (!Array.isArray(evidence)) {
58
+ return [];
59
+ }
60
+ const references = [];
61
+ for (const [index, entry] of evidence.entries()) {
62
+ const reference = readPath(entry, ["reference"]);
63
+ if (typeof reference === "string") {
64
+ references.push({ path: `/evidence/${index}/reference`, value: reference });
65
+ }
66
+ }
67
+ return references;
68
+ }
69
+ /**
70
+ * True when a value under a prohibited property name counts as populated.
71
+ *
72
+ * Booleans are excluded: a credential cannot be `true`, so a boolean under a
73
+ * name like `secret` is a flag. Recognised redaction placeholders are excluded
74
+ * because reporting them punishes the behaviour the specification asks for; that
75
+ * choice is a deliberate false negative, documented in privacy.md §6.
76
+ */
77
+ /**
78
+ * True when a value under a credential-named property is itself a credential.
79
+ *
80
+ * A **scalar** under a name like `password` is the credential. A **container**
81
+ * is a descriptor: `credential: { type: "api-key" }` describes a credential
82
+ * without carrying one, and the identity-and-access-management profile requires
83
+ * exactly that shape for `identity.credential.rotate`. Flagging the container
84
+ * would fire on every conforming credential rotation event, which is how a
85
+ * linter earns the reputation that gets it switched off.
86
+ *
87
+ * Containers are still traversed, so a credential-named member or a
88
+ * credential-shaped value inside one is reported on its own. The cost is a
89
+ * secret stored under a harmless member name inside such a container, which is
90
+ * an accepted false negative documented in privacy.md §6.9.
91
+ */
92
+ function isCredentialValue(value) {
93
+ if (typeof value === "string") {
94
+ return !isRedactionPlaceholder(value);
95
+ }
96
+ return typeof value === "number";
97
+ }
98
+ function isPopulated(value) {
99
+ if (value === null || value === undefined || typeof value === "boolean") {
100
+ return false;
101
+ }
102
+ if (typeof value === "string") {
103
+ return !isRedactionPlaceholder(value);
104
+ }
105
+ if (Array.isArray(value)) {
106
+ return value.length > 0;
107
+ }
108
+ if (typeof value === "object") {
109
+ return Object.keys(value).length > 0;
110
+ }
111
+ return true;
112
+ }
113
+ function finding(rule, path, overrides = {}) {
114
+ return {
115
+ ruleId: rule.id,
116
+ severity: rule.severity,
117
+ confidence: rule.confidence,
118
+ category: rule.category,
119
+ path,
120
+ message: rule.message,
121
+ recommendation: rule.recommendation,
122
+ ...overrides,
123
+ };
124
+ }
125
+ const RULES_BY_ID = new Map(ALL_RULES.map((rule) => [rule.id, rule]));
126
+ /**
127
+ * Applies every value rule to one string.
128
+ *
129
+ * Returns strong findings first; the entropy heuristic is applied only when
130
+ * nothing stronger matched, so a recognised token is never also reported as an
131
+ * anonymous high-entropy candidate.
132
+ */
133
+ function valueFindings(value, path, key) {
134
+ const findings = [];
135
+ if (matchesAuthorizationHeader(value)) {
136
+ findings.push(finding(RULES.AUTHORIZATION_HEADER_VALUE, path));
137
+ }
138
+ if (containsPrivateKeyMaterial(value)) {
139
+ findings.push(finding(RULES.PRIVATE_KEY_MATERIAL, path));
140
+ }
141
+ const tokenRuleId = matchKnownTokenFormat(value);
142
+ if (tokenRuleId !== undefined) {
143
+ const rule = RULES_BY_ID.get(tokenRuleId);
144
+ if (rule !== undefined) {
145
+ findings.push(finding(rule, path));
146
+ }
147
+ }
148
+ const connection = analyzeConnectionString(value);
149
+ if (connection === "credentialed") {
150
+ findings.push(finding(RULES.CONNECTION_STRING_WITH_CREDENTIALS, path));
151
+ }
152
+ else if (connection === "uncredentialed") {
153
+ findings.push(finding(RULES.CONNECTION_STRING_WITHOUT_CREDENTIALS, path));
154
+ }
155
+ // A data-system URL is already described by the connection-string rules;
156
+ // reporting it again as generic userinfo would say the same thing twice.
157
+ const url = analyzeUrl(value);
158
+ if (url?.hasUserinfo === true && !isDataSystemScheme(url.scheme)) {
159
+ findings.push(finding(RULES.URL_USERINFO, path));
160
+ }
161
+ if (findings.length === 0 && isHighEntropyTokenCandidate(value)) {
162
+ findings.push(finding(RULES.HIGH_ENTROPY_TOKEN_CANDIDATE, path, {
163
+ confidence: key !== undefined && isSuspiciousFieldName(key) ? "medium" : "low",
164
+ }));
165
+ }
166
+ return findings;
167
+ }
168
+ /** Applies the property-name rules to one member. */
169
+ function nameFindings(key, value, path) {
170
+ const findings = [];
171
+ if (isProhibitedCredentialFieldName(key) && isCredentialValue(value)) {
172
+ findings.push(finding(RULES.PROHIBITED_CREDENTIAL_FIELD, path));
173
+ }
174
+ if (isRawPayloadFieldName(key) && isPopulated(value)) {
175
+ findings.push(finding(RULES.RAW_PAYLOAD_FIELD, path));
176
+ }
177
+ return findings;
178
+ }
179
+ /** Applies the evidence reference rule. */
180
+ function evidenceFindings(value, path) {
181
+ const url = analyzeUrl(value);
182
+ if (url !== undefined) {
183
+ if (url.hasQuery || url.hasFragment) {
184
+ return [finding(RULES.EVIDENCE_REFERENCE_QUERY, path)];
185
+ }
186
+ return [];
187
+ }
188
+ // References are frequently opaque identifiers or paths rather than URLs, so a
189
+ // question mark is treated as a query string when the value does not parse.
190
+ if (value.includes("?") || value.includes("#")) {
191
+ return [finding(RULES.EVIDENCE_REFERENCE_QUERY, path)];
192
+ }
193
+ return [];
194
+ }
195
+ function severityRank(finding) {
196
+ const index = SEVERITY_ORDER.indexOf(finding.severity);
197
+ return index === -1 ? SEVERITY_ORDER.length : index;
198
+ }
199
+ /** Lints one event and returns its findings, most severe first. */
200
+ export function lintEvent(event, label, validator, options = {}) {
201
+ const eventId = typeof readPath(event, ["id"]) === "string" ? readPath(event, ["id"]) : undefined;
202
+ const identity = eventId === undefined ? {} : { eventId };
203
+ if (options.validateSchema !== false) {
204
+ const issues = validator.validateEvent(event);
205
+ if (issues.length > 0) {
206
+ const shown = issues.slice(0, 5).map((issue) => `${issue.path} ${issue.message}`);
207
+ return {
208
+ label,
209
+ ...identity,
210
+ status: "schema-invalid",
211
+ findings: [],
212
+ schemaIssues: issues.length > shown.length
213
+ ? [...shown, `and ${issues.length - shown.length} further schema issues`]
214
+ : shown,
215
+ };
216
+ }
217
+ }
218
+ const collected = [];
219
+ for (const root of deepRoots(event)) {
220
+ traverse(root.value, root.path, ({ path, key, value }) => {
221
+ if (key !== undefined) {
222
+ collected.push(...nameFindings(key, value, path));
223
+ }
224
+ if (typeof value === "string") {
225
+ collected.push(...valueFindings(value, path, key));
226
+ }
227
+ });
228
+ }
229
+ for (const location of scalarLocations(event)) {
230
+ collected.push(...valueFindings(location.value, location.path, undefined));
231
+ }
232
+ for (const reference of evidenceReferences(event)) {
233
+ collected.push(...evidenceFindings(reference.value, reference.path));
234
+ collected.push(...valueFindings(reference.value, reference.path, undefined));
235
+ }
236
+ for (const root of sizeRoots(event)) {
237
+ const signals = exceededSignals(profileValue(root.value));
238
+ if (signals.length > 0) {
239
+ collected.push(finding(RULES.OVERSIZED_UNFILTERED_VALUE, root.path, {
240
+ message: `${RULES.OVERSIZED_UNFILTERED_VALUE.message} Signals: ${signals.join("; ")}.`,
241
+ }));
242
+ }
243
+ }
244
+ // One finding per rule per location, however many ways it was reached.
245
+ const seen = new Set();
246
+ const findings = collected
247
+ .filter((entry) => {
248
+ const key = `${entry.ruleId}|${entry.path}`;
249
+ if (seen.has(key)) {
250
+ return false;
251
+ }
252
+ seen.add(key);
253
+ return true;
254
+ })
255
+ .map((entry) => ({ ...entry, ...identity }))
256
+ .sort((left, right) => {
257
+ const bySeverity = severityRank(left) - severityRank(right);
258
+ if (bySeverity !== 0) {
259
+ return bySeverity;
260
+ }
261
+ const byPath = left.path.localeCompare(right.path, "en");
262
+ return byPath === 0 ? left.ruleId.localeCompare(right.ruleId, "en") : byPath;
263
+ });
264
+ return {
265
+ label,
266
+ ...identity,
267
+ status: findings.length === 0 ? "clean" : "findings",
268
+ findings,
269
+ schemaIssues: [],
270
+ };
271
+ }
272
+ //# sourceMappingURL=lint-event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint-event.js","sourceRoot":"","sources":["../../../../conformance/src/privacy/lint-event.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The v0.1 privacy rule catalogue.
3
+ *
4
+ * Severity and confidence are properties of the rule, not of an individual
5
+ * finding, so that a report can be filtered predictably. Severity says how bad
6
+ * the risk would be if the suspicion is correct; confidence says how likely the
7
+ * suspicion is to be correct. They are deliberately independent: a field
8
+ * literally named `password` is critical and high-confidence, while a
9
+ * high-entropy string in an arbitrary field is medium and low-confidence.
10
+ *
11
+ * Thresholds and vocabularies are hard-coded in v0.1. Configuration is a future
12
+ * design question; see decisions/0007-deterministic-privacy-linting.md.
13
+ */
14
+ import type { Confidence, Severity } from "./types.js";
15
+ export interface RuleDefinition {
16
+ readonly id: string;
17
+ readonly name: string;
18
+ readonly severity: Severity;
19
+ readonly confidence: Confidence;
20
+ readonly category: string;
21
+ readonly message: string;
22
+ readonly recommendation: string;
23
+ }
24
+ export declare const RULES: {
25
+ readonly PROHIBITED_CREDENTIAL_FIELD: RuleDefinition;
26
+ readonly AUTHORIZATION_HEADER_VALUE: RuleDefinition;
27
+ readonly PRIVATE_KEY_MATERIAL: RuleDefinition;
28
+ readonly JWT_TOKEN: RuleDefinition;
29
+ readonly AWS_ACCESS_KEY_ID: RuleDefinition;
30
+ readonly GITHUB_TOKEN: RuleDefinition;
31
+ readonly GITLAB_TOKEN: RuleDefinition;
32
+ readonly SLACK_TOKEN: RuleDefinition;
33
+ readonly PAYMENT_SECRET_KEY: RuleDefinition;
34
+ readonly CLOUD_API_KEY: RuleDefinition;
35
+ readonly URL_USERINFO: RuleDefinition;
36
+ readonly EVIDENCE_REFERENCE_QUERY: RuleDefinition;
37
+ readonly CONNECTION_STRING_WITH_CREDENTIALS: RuleDefinition;
38
+ readonly CONNECTION_STRING_WITHOUT_CREDENTIALS: RuleDefinition;
39
+ readonly HIGH_ENTROPY_TOKEN_CANDIDATE: RuleDefinition;
40
+ readonly OVERSIZED_UNFILTERED_VALUE: RuleDefinition;
41
+ readonly RAW_PAYLOAD_FIELD: RuleDefinition;
42
+ };
43
+ /** Every rule, ordered by identifier, for documentation and tests. */
44
+ export declare const ALL_RULES: readonly RuleDefinition[];
@@ -0,0 +1,161 @@
1
+ function rule(definition) {
2
+ return definition;
3
+ }
4
+ export const RULES = {
5
+ PROHIBITED_CREDENTIAL_FIELD: rule({
6
+ id: "OAM-PRIV-001",
7
+ name: "prohibited-credential-field",
8
+ severity: "critical",
9
+ confidence: "high",
10
+ category: "credential-field-name",
11
+ message: "A property name associated with credentials carries a non-empty value.",
12
+ recommendation: "Remove the value. Record only the fact of the operation, or a non-sensitive identifier for the credential.",
13
+ }),
14
+ AUTHORIZATION_HEADER_VALUE: rule({
15
+ id: "OAM-PRIV-002",
16
+ name: "authorization-header-value",
17
+ severity: "critical",
18
+ confidence: "high",
19
+ category: "credential-shaped-value",
20
+ message: "A value is shaped like an authorization header credential.",
21
+ recommendation: "Never record authorization headers. Record the authentication method and outcome instead.",
22
+ }),
23
+ PRIVATE_KEY_MATERIAL: rule({
24
+ id: "OAM-PRIV-003",
25
+ name: "private-key-material",
26
+ severity: "critical",
27
+ confidence: "high",
28
+ category: "credential-shaped-value",
29
+ message: "A value contains a marker used by private key material.",
30
+ recommendation: "Remove the key material. Record a key identifier that cannot be used to reconstruct the key.",
31
+ }),
32
+ JWT_TOKEN: rule({
33
+ id: "OAM-PRIV-010",
34
+ name: "jwt-token",
35
+ severity: "critical",
36
+ confidence: "high",
37
+ category: "known-token-format",
38
+ message: "A value is structured as a JSON Web Token: three segments whose header and payload decode to JSON objects.",
39
+ recommendation: "Remove the token. Record the subject identifier and the authentication method instead. Validity was not checked.",
40
+ }),
41
+ AWS_ACCESS_KEY_ID: rule({
42
+ id: "OAM-PRIV-011",
43
+ name: "aws-access-key-id",
44
+ severity: "high",
45
+ confidence: "high",
46
+ category: "known-token-format",
47
+ message: "A value matches the published shape of a cloud access key identifier. An identifier alone is not a complete credential.",
48
+ recommendation: "Remove the identifier, or record a non-sensitive reference to the principal it belongs to.",
49
+ }),
50
+ GITHUB_TOKEN: rule({
51
+ id: "OAM-PRIV-012",
52
+ name: "github-token",
53
+ severity: "critical",
54
+ confidence: "high",
55
+ category: "known-token-format",
56
+ message: "A value matches a published personal access token prefix.",
57
+ recommendation: "Remove the token and rotate it. Validity was not checked.",
58
+ }),
59
+ GITLAB_TOKEN: rule({
60
+ id: "OAM-PRIV-013",
61
+ name: "gitlab-token",
62
+ severity: "critical",
63
+ confidence: "high",
64
+ category: "known-token-format",
65
+ message: "A value matches a published personal access token prefix.",
66
+ recommendation: "Remove the token and rotate it. Validity was not checked.",
67
+ }),
68
+ SLACK_TOKEN: rule({
69
+ id: "OAM-PRIV-014",
70
+ name: "slack-token",
71
+ severity: "critical",
72
+ confidence: "high",
73
+ category: "known-token-format",
74
+ message: "A value matches a published messaging platform token prefix.",
75
+ recommendation: "Remove the token and rotate it. Validity was not checked.",
76
+ }),
77
+ PAYMENT_SECRET_KEY: rule({
78
+ id: "OAM-PRIV-015",
79
+ name: "payment-secret-key",
80
+ severity: "critical",
81
+ confidence: "high",
82
+ category: "known-token-format",
83
+ message: "A value matches a published secret API key prefix.",
84
+ recommendation: "Remove the key and rotate it. Validity was not checked.",
85
+ }),
86
+ CLOUD_API_KEY: rule({
87
+ id: "OAM-PRIV-016",
88
+ name: "cloud-api-key",
89
+ severity: "critical",
90
+ confidence: "high",
91
+ category: "known-token-format",
92
+ message: "A value matches a published API key prefix.",
93
+ recommendation: "Remove the key and rotate it. Validity was not checked.",
94
+ }),
95
+ URL_USERINFO: rule({
96
+ id: "OAM-PRIV-030",
97
+ name: "url-userinfo",
98
+ severity: "critical",
99
+ confidence: "high",
100
+ category: "url",
101
+ message: "A URL embeds user information, which commonly carries a password.",
102
+ recommendation: "Remove the credentials from the URL. Record the location without any embedded user information.",
103
+ }),
104
+ EVIDENCE_REFERENCE_QUERY: rule({
105
+ id: "OAM-PRIV-031",
106
+ name: "evidence-reference-query",
107
+ severity: "medium",
108
+ confidence: "medium",
109
+ category: "url",
110
+ message: "An evidence reference carries a query string or fragment, which may hold signed access parameters, identifiers or search terms.",
111
+ recommendation: "Store a stable, access-controlled reference without embedded parameters. A query string is not always sensitive; review this one.",
112
+ }),
113
+ CONNECTION_STRING_WITH_CREDENTIALS: rule({
114
+ id: "OAM-PRIV-040",
115
+ name: "connection-string-with-credentials",
116
+ severity: "critical",
117
+ confidence: "high",
118
+ category: "connection-string",
119
+ message: "A value is shaped like a connection string that carries a password.",
120
+ recommendation: "Remove the connection string and rotate the credential it exposes.",
121
+ }),
122
+ CONNECTION_STRING_WITHOUT_CREDENTIALS: rule({
123
+ id: "OAM-PRIV-041",
124
+ name: "connection-string-without-credentials",
125
+ severity: "low",
126
+ confidence: "medium",
127
+ category: "connection-string",
128
+ message: "A value is shaped like a connection string. No credential was detected in it; it may still disclose internal infrastructure.",
129
+ recommendation: "Record a logical name for the system rather than a connection string. This is not reported as a credential.",
130
+ }),
131
+ HIGH_ENTROPY_TOKEN_CANDIDATE: rule({
132
+ id: "OAM-PRIV-050",
133
+ name: "high-entropy-token-candidate",
134
+ severity: "medium",
135
+ confidence: "low",
136
+ category: "heuristic",
137
+ message: "A value is long, has no whitespace and has high character entropy, which is consistent with a token or key.",
138
+ recommendation: "Confirm what this value is. Many high-entropy values are legitimate identifiers; this rule is a heuristic and is expected to produce false positives.",
139
+ }),
140
+ OVERSIZED_UNFILTERED_VALUE: rule({
141
+ id: "OAM-PRIV-060",
142
+ name: "oversized-unfiltered-value",
143
+ severity: "medium",
144
+ confidence: "medium",
145
+ category: "minimization",
146
+ message: "A value is large enough to suggest that an object was captured rather than selected.",
147
+ recommendation: "Record the fields the audit purpose requires. Prefer changed field names, hashes or references over whole objects.",
148
+ }),
149
+ RAW_PAYLOAD_FIELD: rule({
150
+ id: "OAM-PRIV-061",
151
+ name: "raw-payload-field",
152
+ severity: "high",
153
+ confidence: "high",
154
+ category: "minimization",
155
+ message: "A property name associated with raw request, response or message bodies is populated.",
156
+ recommendation: "Do not capture whole payloads. Record selected, named fields, or a reference to the payload held under its own controls.",
157
+ }),
158
+ };
159
+ /** Every rule, ordered by identifier, for documentation and tests. */
160
+ export const ALL_RULES = Object.values(RULES).sort((left, right) => left.id.localeCompare(right.id, "en"));
161
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../../../conformance/src/privacy/rules.ts"],"names":[],"mappings":"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"}