@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,40 @@
1
+ /**
2
+ * URL and connection-string analysis.
3
+ *
4
+ * Parsing is done with the WHATWG `URL` parser rather than a regular
5
+ * expression. URL grammar is genuinely difficult, and a regular expression that
6
+ * looks right will disagree with a real parser on exactly the inputs an attacker
7
+ * chooses. Nothing here resolves, fetches or opens anything: the parser is used
8
+ * as a grammar, offline.
9
+ */
10
+ /** What was found in a value that parses as a URL. */
11
+ export interface UrlAnalysis {
12
+ readonly scheme: string;
13
+ readonly hasUserinfo: boolean;
14
+ readonly hasPassword: boolean;
15
+ readonly hasQuery: boolean;
16
+ readonly hasFragment: boolean;
17
+ }
18
+ /** Parses a value as a URL and reports what it contains. Returns `undefined` if it is not a URL. */
19
+ export declare function analyzeUrl(value: string): UrlAnalysis | undefined;
20
+ /** True when a URL scheme identifies a data store or message broker. */
21
+ export declare function isDataSystemScheme(scheme: string): boolean;
22
+ export type ConnectionStringKind = "credentialed" | "uncredentialed";
23
+ /**
24
+ * Recognises a key-value connection string such as
25
+ * `Server=...;Database=...;User Id=...;Password=...`.
26
+ *
27
+ * At least two recognised keys are required, so that an arbitrary
28
+ * semicolon-separated string is not mistaken for one. The pattern is a single
29
+ * bounded alternation over one key-value pair and cannot backtrack
30
+ * catastrophically.
31
+ */
32
+ export declare function analyzeKeyValueConnectionString(value: string): ConnectionStringKind | undefined;
33
+ /**
34
+ * Recognises a connection string in either URL or key-value form.
35
+ *
36
+ * A connection string without a credential is still reported, separately and at
37
+ * low severity: it discloses infrastructure, but calling it a credential when it
38
+ * carries none would be wrong.
39
+ */
40
+ export declare function analyzeConnectionString(value: string): ConnectionStringKind | undefined;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * URL and connection-string analysis.
3
+ *
4
+ * Parsing is done with the WHATWG `URL` parser rather than a regular
5
+ * expression. URL grammar is genuinely difficult, and a regular expression that
6
+ * looks right will disagree with a real parser on exactly the inputs an attacker
7
+ * chooses. Nothing here resolves, fetches or opens anything: the parser is used
8
+ * as a grammar, offline.
9
+ */
10
+ /**
11
+ * Schemes whose URL form is a connection string: a locator for a data store or
12
+ * broker, which commonly carries credentials.
13
+ */
14
+ const DATA_SYSTEM_SCHEMES = new Set([
15
+ "postgres:",
16
+ "postgresql:",
17
+ "mysql:",
18
+ "mariadb:",
19
+ "mongodb:",
20
+ "mongodb+srv:",
21
+ "redis:",
22
+ "rediss:",
23
+ "amqp:",
24
+ "amqps:",
25
+ "mssql:",
26
+ "sqlserver:",
27
+ "oracle:",
28
+ "jdbc:",
29
+ "cassandra:",
30
+ "clickhouse:",
31
+ "db2:",
32
+ "ftp:",
33
+ "ftps:",
34
+ "sftp:",
35
+ "smb:",
36
+ ]);
37
+ /** Keys that appear in key-value connection strings. */
38
+ const CONNECTION_STRING_KEYS = new Set([
39
+ "server",
40
+ "host",
41
+ "hostname",
42
+ "datasource",
43
+ "initialcatalog",
44
+ "database",
45
+ "userid",
46
+ "uid",
47
+ "username",
48
+ "user",
49
+ "password",
50
+ "pwd",
51
+ "port",
52
+ "endpoint",
53
+ "accountname",
54
+ "accountkey",
55
+ "sharedaccesskey",
56
+ "integratedsecurity",
57
+ "encrypt",
58
+ "trustservercertificate",
59
+ ]);
60
+ /** Keys whose presence with a non-empty value means the string carries a credential. */
61
+ const CONNECTION_STRING_SECRET_KEYS = new Set([
62
+ "password",
63
+ "pwd",
64
+ "accountkey",
65
+ "sharedaccesskey",
66
+ ]);
67
+ /** Parses a value as a URL and reports what it contains. Returns `undefined` if it is not a URL. */
68
+ export function analyzeUrl(value) {
69
+ const trimmed = value.trim();
70
+ if (!trimmed.includes(":")) {
71
+ return undefined;
72
+ }
73
+ let url;
74
+ try {
75
+ url = new URL(trimmed);
76
+ }
77
+ catch {
78
+ return undefined;
79
+ }
80
+ return {
81
+ scheme: url.protocol,
82
+ hasUserinfo: url.username !== "" || url.password !== "",
83
+ hasPassword: url.password !== "",
84
+ hasQuery: url.search !== "",
85
+ hasFragment: url.hash !== "",
86
+ };
87
+ }
88
+ /** True when a URL scheme identifies a data store or message broker. */
89
+ export function isDataSystemScheme(scheme) {
90
+ return DATA_SYSTEM_SCHEMES.has(scheme.toLowerCase());
91
+ }
92
+ /**
93
+ * Recognises a key-value connection string such as
94
+ * `Server=...;Database=...;User Id=...;Password=...`.
95
+ *
96
+ * At least two recognised keys are required, so that an arbitrary
97
+ * semicolon-separated string is not mistaken for one. The pattern is a single
98
+ * bounded alternation over one key-value pair and cannot backtrack
99
+ * catastrophically.
100
+ */
101
+ export function analyzeKeyValueConnectionString(value) {
102
+ if (!value.includes("=") || !value.includes(";")) {
103
+ return undefined;
104
+ }
105
+ if (value.length > 8192) {
106
+ return undefined;
107
+ }
108
+ let recognisedKeys = 0;
109
+ let credentialed = false;
110
+ for (const pair of value.split(";")) {
111
+ const separator = pair.indexOf("=");
112
+ if (separator <= 0) {
113
+ continue;
114
+ }
115
+ const key = pair
116
+ .slice(0, separator)
117
+ .trim()
118
+ .toLowerCase()
119
+ .replaceAll(/[-_. ]/g, "");
120
+ const hasValue = pair.slice(separator + 1).trim() !== "";
121
+ if (!CONNECTION_STRING_KEYS.has(key)) {
122
+ continue;
123
+ }
124
+ recognisedKeys += 1;
125
+ if (CONNECTION_STRING_SECRET_KEYS.has(key) && hasValue) {
126
+ credentialed = true;
127
+ }
128
+ }
129
+ if (recognisedKeys < 2) {
130
+ return undefined;
131
+ }
132
+ return credentialed ? "credentialed" : "uncredentialed";
133
+ }
134
+ /**
135
+ * Recognises a connection string in either URL or key-value form.
136
+ *
137
+ * A connection string without a credential is still reported, separately and at
138
+ * low severity: it discloses infrastructure, but calling it a credential when it
139
+ * carries none would be wrong.
140
+ */
141
+ export function analyzeConnectionString(value) {
142
+ const url = analyzeUrl(value);
143
+ if (url !== undefined && isDataSystemScheme(url.scheme)) {
144
+ return url.hasPassword ? "credentialed" : "uncredentialed";
145
+ }
146
+ return analyzeKeyValueConnectionString(value);
147
+ }
148
+ //# sourceMappingURL=url-analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-analysis.js","sourceRoot":"","sources":["../../../../conformance/src/privacy/url-analysis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Profile conformance checking for a single event.
3
+ *
4
+ * The order is fixed and matters: core validation first, then rule selection,
5
+ * then evaluation. An event that does not conform to the core model is reported
6
+ * as core-invalid and its profile rules are **not** evaluated, because a profile
7
+ * only ever adds requirements to a conforming event. Reporting profile
8
+ * conformance for an event the core rejects would be the one thing a profile
9
+ * must never do.
10
+ */
11
+ import type { EventValidator } from "../validate-core.js";
12
+ import { type ProfileCheckResult, type ProfileDefinition } from "./types.js";
13
+ export interface CheckProfileOptions {
14
+ /** Validate against the canonical core schema first. Defaults to true. */
15
+ readonly validateCore?: boolean;
16
+ }
17
+ /**
18
+ * Checks one event against one profile.
19
+ *
20
+ * The input event is never modified: every step reads.
21
+ */
22
+ export declare function checkProfile(event: unknown, label: string, profile: ProfileDefinition, validator: EventValidator, options?: CheckProfileOptions): ProfileCheckResult;
@@ -0,0 +1,72 @@
1
+ import { resolvePointer } from "./resolve-pointer.js";
2
+ import { eventName, selectRules } from "./select-rules.js";
3
+ import { evaluateRule } from "./evaluate-rule.js";
4
+ import { supportsCoreVersion, } from "./types.js";
5
+ function eventIdentity(event) {
6
+ const resolved = resolvePointer(event, "/id");
7
+ return typeof resolved.value === "string" ? { eventId: resolved.value } : {};
8
+ }
9
+ /**
10
+ * Checks one event against one profile.
11
+ *
12
+ * The input event is never modified: every step reads.
13
+ */
14
+ export function checkProfile(event, label, profile, validator, options = {}) {
15
+ const identity = eventIdentity(event);
16
+ const descriptor = { name: profile.name, version: profile.version };
17
+ const base = {
18
+ label,
19
+ ...identity,
20
+ profile: descriptor,
21
+ matchedRules: [],
22
+ errors: [],
23
+ warnings: [],
24
+ coreIssues: [],
25
+ };
26
+ if (options.validateCore !== false) {
27
+ const issues = validator.validateEvent(event);
28
+ if (issues.length > 0) {
29
+ const shown = issues.slice(0, 5).map((issue) => `${issue.path} ${issue.message}`);
30
+ return {
31
+ ...base,
32
+ status: "core-invalid",
33
+ coreValid: false,
34
+ profileValid: false,
35
+ coreIssues: issues.length > shown.length
36
+ ? [...shown, `and ${issues.length - shown.length} further core schema issues`]
37
+ : shown,
38
+ };
39
+ }
40
+ }
41
+ // A profile states which core versions it applies to. An event declaring
42
+ // another version is outside its scope, not in violation of it.
43
+ const specVersion = resolvePointer(event, "/specVersion").value;
44
+ if (!supportsCoreVersion(profile, specVersion)) {
45
+ return { ...base, status: "not-applicable", coreValid: true, profileValid: true };
46
+ }
47
+ const name = eventName(event);
48
+ if (name === undefined) {
49
+ return { ...base, status: "not-applicable", coreValid: true, profileValid: true };
50
+ }
51
+ const rules = selectRules(profile, name);
52
+ if (rules.length === 0) {
53
+ return { ...base, status: "not-applicable", coreValid: true, profileValid: true };
54
+ }
55
+ const errors = [];
56
+ const warnings = [];
57
+ for (const rule of rules) {
58
+ const evaluation = evaluateRule(event, rule);
59
+ errors.push(...evaluation.errors);
60
+ warnings.push(...evaluation.warnings);
61
+ }
62
+ return {
63
+ ...base,
64
+ status: errors.length === 0 ? "conforming" : "violations",
65
+ coreValid: true,
66
+ profileValid: errors.length === 0,
67
+ matchedRules: rules.map((rule) => rule.id),
68
+ errors,
69
+ warnings,
70
+ };
71
+ }
72
+ //# sourceMappingURL=check-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-profile.js","sourceRoot":"","sources":["../../../../conformance/src/profiles/check-profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { Condition, ProfileFinding, ProfileRule, RuleSeverity } from "./types.js";
2
+ /** Default severity for a rule that does not declare one. */
3
+ export declare const DEFAULT_RULE_SEVERITY: RuleSeverity;
4
+ /**
5
+ * Whether a rule's condition holds.
6
+ *
7
+ * A condition is one path compared for equality against one scalar. When the
8
+ * path is absent the condition does **not** hold, so a conditional requirement
9
+ * contributes nothing rather than firing on missing data. That is deterministic
10
+ * and it is also the safer direction: a profile that demanded approval whenever
11
+ * it could not tell whether a role was privileged would fail every event that
12
+ * omitted the flag, including events the rule was never meant to govern.
13
+ */
14
+ export declare function conditionHolds(event: unknown, condition: Condition): boolean;
15
+ export interface RuleEvaluation {
16
+ readonly applied: boolean;
17
+ readonly errors: readonly ProfileFinding[];
18
+ readonly warnings: readonly ProfileFinding[];
19
+ }
20
+ /**
21
+ * Evaluates one rule against one event.
22
+ *
23
+ * `applied` is false when the rule's condition did not hold; the rule still
24
+ * counts as matched, because it governs the event, but it contributed no
25
+ * requirements.
26
+ */
27
+ export declare function evaluateRule(event: unknown, rule: ProfileRule): RuleEvaluation;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Rule evaluation.
3
+ *
4
+ * Evaluation reads values and compares them. It never executes anything from a
5
+ * profile definition, never builds a function, never interprets an expression
6
+ * and never reaches outside the event it was given. Every check below is a
7
+ * presence test, a JSON type test or a strict scalar equality.
8
+ */
9
+ import { concatPointer, isPresent, matchesMetadataType, jsonTypeOf, resolvePointer, } from "./resolve-pointer.js";
10
+ const METADATA_POINTER = "/metadata";
11
+ /** Default severity for a rule that does not declare one. */
12
+ export const DEFAULT_RULE_SEVERITY = "error";
13
+ /**
14
+ * Whether a rule's condition holds.
15
+ *
16
+ * A condition is one path compared for equality against one scalar. When the
17
+ * path is absent the condition does **not** hold, so a conditional requirement
18
+ * contributes nothing rather than firing on missing data. That is deterministic
19
+ * and it is also the safer direction: a profile that demanded approval whenever
20
+ * it could not tell whether a role was privileged would fail every event that
21
+ * omitted the flag, including events the rule was never meant to govern.
22
+ */
23
+ export function conditionHolds(event, condition) {
24
+ const resolved = resolvePointer(event, condition.path);
25
+ if (!resolved.found) {
26
+ return false;
27
+ }
28
+ return Object.is(resolved.value, condition.equals);
29
+ }
30
+ function finding(ruleId, path, message, severity) {
31
+ return { ruleId, path, message, severity };
32
+ }
33
+ /**
34
+ * Evaluates one rule against one event.
35
+ *
36
+ * `applied` is false when the rule's condition did not hold; the rule still
37
+ * counts as matched, because it governs the event, but it contributed no
38
+ * requirements.
39
+ */
40
+ export function evaluateRule(event, rule) {
41
+ if (rule.when !== undefined && !conditionHolds(event, rule.when)) {
42
+ return { applied: false, errors: [], warnings: [] };
43
+ }
44
+ const severity = rule.severity ?? DEFAULT_RULE_SEVERITY;
45
+ const failures = [];
46
+ const warnings = [];
47
+ for (const pointer of rule.requiredPaths ?? []) {
48
+ if (!isPresent(resolvePointer(event, pointer))) {
49
+ failures.push(finding(rule.id, pointer, "required by the profile, but absent or empty", severity));
50
+ }
51
+ }
52
+ for (const requirement of rule.requiredMetadata ?? []) {
53
+ const pointer = concatPointer(METADATA_POINTER, requirement.path);
54
+ const resolved = resolvePointer(event, pointer);
55
+ if (!isPresent(resolved)) {
56
+ failures.push(finding(rule.id, pointer, `required metadata of type "${requirement.type}", but absent or empty`, severity));
57
+ continue;
58
+ }
59
+ if (!matchesMetadataType(resolved.value, requirement.type)) {
60
+ failures.push(finding(rule.id, pointer, `required metadata must be of type "${requirement.type}", but is "${jsonTypeOf(resolved.value)}"`, severity));
61
+ }
62
+ }
63
+ for (const requirement of rule.requiredValues ?? []) {
64
+ const resolved = resolvePointer(event, requirement.path);
65
+ if (!resolved.found) {
66
+ failures.push(finding(rule.id, requirement.path, `required by the profile to equal ${JSON.stringify(requirement.equals)}, but absent`, severity));
67
+ continue;
68
+ }
69
+ if (!Object.is(resolved.value, requirement.equals)) {
70
+ // The expected value comes from the profile definition, not from the
71
+ // event, so quoting it discloses nothing about the event.
72
+ failures.push(finding(rule.id, requirement.path, `required by the profile to equal ${JSON.stringify(requirement.equals)}`, severity));
73
+ }
74
+ }
75
+ for (const pointer of rule.recommendedPaths ?? []) {
76
+ if (!isPresent(resolvePointer(event, pointer))) {
77
+ warnings.push(finding(rule.id, pointer, "recommended by the profile, but absent or empty", "warning"));
78
+ }
79
+ }
80
+ // A rule declared `info` or `warning` cannot fail conformance; its
81
+ // requirement violations are reported alongside the recommendations.
82
+ if (severity === "error") {
83
+ return { applied: true, errors: failures, warnings };
84
+ }
85
+ return { applied: true, errors: [], warnings: [...failures, ...warnings] };
86
+ }
87
+ //# sourceMappingURL=evaluate-rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate-rule.js","sourceRoot":"","sources":["../../../../conformance/src/profiles/evaluate-rule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { supportsCoreVersion, type ProfileDefinition } from "./types.js";
2
+ /** Re-exported so that existing import sites keep working. */
3
+ export { supportsCoreVersion };
4
+ /** File name a profile definition must use inside its directory. */
5
+ export declare const PROFILE_DEFINITION_FILE = "profile.json";
6
+ /**
7
+ * Largest profile definition that will be read. A profile is a small document;
8
+ * anything larger is a mistake or an attempt to exhaust memory.
9
+ */
10
+ export declare const MAX_PROFILE_BYTES: number;
11
+ export type ProfileLoadResult = {
12
+ readonly ok: true;
13
+ readonly profile: ProfileDefinition;
14
+ readonly file: string;
15
+ } | {
16
+ readonly ok: false;
17
+ readonly error: string;
18
+ readonly issues?: readonly string[];
19
+ };
20
+ /** Absolute path of the directory profiles are loaded from. */
21
+ export declare function profilesDirectory(): string;
22
+ /** Names of the profiles that ship with the repository and carry a definition. */
23
+ export declare function availableProfiles(): string[];
24
+ /**
25
+ * Loads a profile definition by name and validates it.
26
+ *
27
+ * The name is checked against a strict token pattern and joined to the profiles
28
+ * directory, so `../` and absolute paths cannot escape it.
29
+ */
30
+ export declare function loadProfile(name: string): ProfileLoadResult;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Loading of profile definitions.
3
+ *
4
+ * Profiles are loaded **only** from the repository's own `profiles/` directory,
5
+ * by name, from a fixed file name. There is no path argument, no remote
6
+ * registry and no reference resolution: a profile is a set of requirements that
7
+ * decides whether an audit event is conforming, and letting a caller point that
8
+ * at an arbitrary file makes conformance mean whatever the caller wants.
9
+ */
10
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
11
+ import path from "node:path";
12
+ import { repositoryRoot, validateProfileDefinition } from "./validate-profile-definition.js";
13
+ import { supportsCoreVersion } from "./types.js";
14
+ /** Re-exported so that existing import sites keep working. */
15
+ export { supportsCoreVersion };
16
+ /** File name a profile definition must use inside its directory. */
17
+ export const PROFILE_DEFINITION_FILE = "profile.json";
18
+ /**
19
+ * Largest profile definition that will be read. A profile is a small document;
20
+ * anything larger is a mistake or an attempt to exhaust memory.
21
+ */
22
+ export const MAX_PROFILE_BYTES = 256 * 1024;
23
+ /** Profile names are directory names, restricted to the same token form as the schema. */
24
+ const PROFILE_NAME = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
25
+ /** Absolute path of the directory profiles are loaded from. */
26
+ export function profilesDirectory() {
27
+ return path.join(repositoryRoot(), "profiles");
28
+ }
29
+ /** Names of the profiles that ship with the repository and carry a definition. */
30
+ export function availableProfiles() {
31
+ const root = profilesDirectory();
32
+ if (!existsSync(root)) {
33
+ return [];
34
+ }
35
+ return readdirSync(root, { withFileTypes: true })
36
+ .filter((entry) => entry.isDirectory())
37
+ .map((entry) => entry.name)
38
+ .filter((name) => existsSync(path.join(root, name, PROFILE_DEFINITION_FILE)))
39
+ .sort((left, right) => left.localeCompare(right, "en"));
40
+ }
41
+ /**
42
+ * Loads a profile definition by name and validates it.
43
+ *
44
+ * The name is checked against a strict token pattern and joined to the profiles
45
+ * directory, so `../` and absolute paths cannot escape it.
46
+ */
47
+ export function loadProfile(name) {
48
+ if (!PROFILE_NAME.test(name)) {
49
+ return {
50
+ ok: false,
51
+ error: `"${name}" is not a valid profile name`,
52
+ };
53
+ }
54
+ const file = path.join(profilesDirectory(), name, PROFILE_DEFINITION_FILE);
55
+ if (!existsSync(file)) {
56
+ const available = availableProfiles();
57
+ return {
58
+ ok: false,
59
+ error: available.length === 0
60
+ ? `no profile named "${name}" is available`
61
+ : `no profile named "${name}"; available profiles: ${available.join(", ")}`,
62
+ };
63
+ }
64
+ const size = statSync(file).size;
65
+ if (size > MAX_PROFILE_BYTES) {
66
+ return {
67
+ ok: false,
68
+ error: `profile definition is ${size} bytes, above the ${MAX_PROFILE_BYTES} byte limit`,
69
+ };
70
+ }
71
+ let parsed;
72
+ try {
73
+ parsed = JSON.parse(readFileSync(file, "utf8"));
74
+ }
75
+ catch (cause) {
76
+ return { ok: false, error: `cannot parse profile definition: ${cause.message}` };
77
+ }
78
+ const issues = validateProfileDefinition(parsed);
79
+ if (issues.length > 0) {
80
+ return {
81
+ ok: false,
82
+ error: `profile definition "${name}" is not valid`,
83
+ issues: issues.map((issue) => `${issue.path} ${issue.message}`),
84
+ };
85
+ }
86
+ const profile = parsed;
87
+ if (profile.name !== name) {
88
+ return {
89
+ ok: false,
90
+ error: `profile definition declares name "${profile.name}" but lives in directory "${name}"`,
91
+ };
92
+ }
93
+ return { ok: true, profile, file };
94
+ }
95
+ //# sourceMappingURL=load-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-profile.js","sourceRoot":"","sources":["../../../../conformance/src/profiles/load-profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * RFC 6901 JSON Pointer resolution.
3
+ *
4
+ * Resolution reads own enumerable properties only. It never walks a prototype,
5
+ * never invokes an accessor and never evaluates anything: a profile definition
6
+ * is data, and a pointer taken from one must not be able to reach `__proto__`,
7
+ * `constructor` or any inherited member of the event being checked.
8
+ */
9
+ /** Deepest pointer accepted, so that a hostile pointer cannot drive unbounded work. */
10
+ export declare const MAX_POINTER_DEPTH = 32;
11
+ export interface PointerResolution {
12
+ /** Whether the pointer resolved to a member that exists. */
13
+ readonly found: boolean;
14
+ readonly value: unknown;
15
+ }
16
+ /** Decodes one reference token: `~1` to `/`, then `~0` to `~`, in that order. */
17
+ export declare function decodeReferenceToken(token: string): string;
18
+ /** Splits a pointer into its decoded reference tokens, or `undefined` if malformed. */
19
+ export declare function pointerTokens(pointer: string): string[] | undefined;
20
+ /** Joins a base pointer and a relative pointer, as `/metadata` + `/role/id`. */
21
+ export declare function concatPointer(base: string, relative: string): string;
22
+ /**
23
+ * Resolves a JSON Pointer against a parsed JSON value.
24
+ *
25
+ * Returns `found: false` for a malformed pointer, a member that does not exist,
26
+ * an out-of-range array index, or any attempt to traverse a non-container.
27
+ */
28
+ export declare function resolvePointer(root: unknown, pointer: string): PointerResolution;
29
+ /**
30
+ * Whether a resolved value counts as **present** for a profile requirement.
31
+ *
32
+ * Present means: the member exists, is not null, and is not an empty string,
33
+ * array or object. `false` and `0` are present — they are answers, not absences,
34
+ * and a profile that could not require `mfa: false` to be recorded would be
35
+ * unable to distinguish "not recorded" from "recorded as false".
36
+ *
37
+ * This differs deliberately from the privacy linter's notion of a populated
38
+ * value, which excludes booleans because a credential cannot be `true`.
39
+ */
40
+ export declare function isPresent(resolution: PointerResolution): boolean;
41
+ /** JSON type of a value, using the profile metadata type vocabulary. */
42
+ export declare function jsonTypeOf(value: unknown): string;
43
+ /** Whether a value matches a declared metadata type. `integer` is a subset of `number`. */
44
+ export declare function matchesMetadataType(value: unknown, expected: string): boolean;