@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.
- package/LICENSE +215 -0
- package/README.md +647 -0
- package/dist/conformance/src/cli.d.ts +20 -0
- package/dist/conformance/src/cli.js +560 -0
- package/dist/conformance/src/cli.js.map +1 -0
- package/dist/conformance/src/format-errors.d.ts +20 -0
- package/dist/conformance/src/format-errors.js +116 -0
- package/dist/conformance/src/format-errors.js.map +1 -0
- package/dist/conformance/src/integrity/canonicalize.d.ts +25 -0
- package/dist/conformance/src/integrity/canonicalize.js +100 -0
- package/dist/conformance/src/integrity/canonicalize.js.map +1 -0
- package/dist/conformance/src/integrity/digest.d.ts +54 -0
- package/dist/conformance/src/integrity/digest.js +126 -0
- package/dist/conformance/src/integrity/digest.js.map +1 -0
- package/dist/conformance/src/integrity/types.d.ts +86 -0
- package/dist/conformance/src/integrity/types.js +36 -0
- package/dist/conformance/src/integrity/types.js.map +1 -0
- package/dist/conformance/src/integrity/verify-chain.d.ts +23 -0
- package/dist/conformance/src/integrity/verify-chain.js +235 -0
- package/dist/conformance/src/integrity/verify-chain.js.map +1 -0
- package/dist/conformance/src/integrity/verify-event.d.ts +29 -0
- package/dist/conformance/src/integrity/verify-event.js +140 -0
- package/dist/conformance/src/integrity/verify-event.js.map +1 -0
- package/dist/conformance/src/privacy/entropy.d.ts +14 -0
- package/dist/conformance/src/privacy/entropy.js +86 -0
- package/dist/conformance/src/privacy/entropy.js.map +1 -0
- package/dist/conformance/src/privacy/field-names.d.ts +36 -0
- package/dist/conformance/src/privacy/field-names.js +121 -0
- package/dist/conformance/src/privacy/field-names.js.map +1 -0
- package/dist/conformance/src/privacy/lint-event.d.ts +18 -0
- package/dist/conformance/src/privacy/lint-event.js +272 -0
- package/dist/conformance/src/privacy/lint-event.js.map +1 -0
- package/dist/conformance/src/privacy/rules.d.ts +44 -0
- package/dist/conformance/src/privacy/rules.js +161 -0
- package/dist/conformance/src/privacy/rules.js.map +1 -0
- package/dist/conformance/src/privacy/safe-formats.d.ts +32 -0
- package/dist/conformance/src/privacy/safe-formats.js +118 -0
- package/dist/conformance/src/privacy/safe-formats.js.map +1 -0
- package/dist/conformance/src/privacy/size-analysis.d.ts +36 -0
- package/dist/conformance/src/privacy/size-analysis.js +87 -0
- package/dist/conformance/src/privacy/size-analysis.js.map +1 -0
- package/dist/conformance/src/privacy/token-patterns.d.ts +24 -0
- package/dist/conformance/src/privacy/token-patterns.js +126 -0
- package/dist/conformance/src/privacy/token-patterns.js.map +1 -0
- package/dist/conformance/src/privacy/traverse.d.ts +33 -0
- package/dist/conformance/src/privacy/traverse.js +61 -0
- package/dist/conformance/src/privacy/traverse.js.map +1 -0
- package/dist/conformance/src/privacy/types.d.ts +58 -0
- package/dist/conformance/src/privacy/types.js +32 -0
- package/dist/conformance/src/privacy/types.js.map +1 -0
- package/dist/conformance/src/privacy/url-analysis.d.ts +40 -0
- package/dist/conformance/src/privacy/url-analysis.js +148 -0
- package/dist/conformance/src/privacy/url-analysis.js.map +1 -0
- package/dist/conformance/src/profiles/check-profile.d.ts +22 -0
- package/dist/conformance/src/profiles/check-profile.js +72 -0
- package/dist/conformance/src/profiles/check-profile.js.map +1 -0
- package/dist/conformance/src/profiles/evaluate-rule.d.ts +27 -0
- package/dist/conformance/src/profiles/evaluate-rule.js +87 -0
- package/dist/conformance/src/profiles/evaluate-rule.js.map +1 -0
- package/dist/conformance/src/profiles/load-profile.d.ts +30 -0
- package/dist/conformance/src/profiles/load-profile.js +95 -0
- package/dist/conformance/src/profiles/load-profile.js.map +1 -0
- package/dist/conformance/src/profiles/resolve-pointer.d.ts +44 -0
- package/dist/conformance/src/profiles/resolve-pointer.js +123 -0
- package/dist/conformance/src/profiles/resolve-pointer.js.map +1 -0
- package/dist/conformance/src/profiles/select-rules.d.ts +13 -0
- package/dist/conformance/src/profiles/select-rules.js +42 -0
- package/dist/conformance/src/profiles/select-rules.js.map +1 -0
- package/dist/conformance/src/profiles/types.d.ts +101 -0
- package/dist/conformance/src/profiles/types.js +31 -0
- package/dist/conformance/src/profiles/types.js.map +1 -0
- package/dist/conformance/src/profiles/validate-profile-definition.d.ts +12 -0
- package/dist/conformance/src/profiles/validate-profile-definition.js +47 -0
- package/dist/conformance/src/profiles/validate-profile-definition.js.map +1 -0
- package/dist/conformance/src/sources.d.ts +53 -0
- package/dist/conformance/src/sources.js +153 -0
- package/dist/conformance/src/sources.js.map +1 -0
- package/dist/conformance/src/validate-core.d.ts +30 -0
- package/dist/conformance/src/validate-core.js +62 -0
- package/dist/conformance/src/validate-core.js.map +1 -0
- package/dist/conformance/src/validate.d.ts +34 -0
- package/dist/conformance/src/validate.js +62 -0
- package/dist/conformance/src/validate.js.map +1 -0
- package/dist/conformance/src/validator-interface.d.ts +35 -0
- package/dist/conformance/src/validator-interface.js +19 -0
- package/dist/conformance/src/validator-interface.js.map +1 -0
- package/package.json +85 -0
- package/profiles/README.md +201 -0
- package/profiles/api-and-integration-management/README.md +271 -0
- package/profiles/api-and-integration-management/profile.json +258 -0
- package/profiles/backup-and-recovery/README.md +318 -0
- package/profiles/backup-and-recovery/profile.json +178 -0
- package/profiles/customer-and-account-management/README.md +340 -0
- package/profiles/customer-and-account-management/profile.json +237 -0
- package/profiles/deployment-and-change-management/README.md +359 -0
- package/profiles/deployment-and-change-management/profile.json +298 -0
- package/profiles/document-management/README.md +120 -0
- package/profiles/document-management/profile.json +170 -0
- package/profiles/financial-transaction-management/README.md +339 -0
- package/profiles/financial-transaction-management/profile.json +247 -0
- package/profiles/identity-and-access-management/README.md +112 -0
- package/profiles/identity-and-access-management/profile.json +120 -0
- package/profiles/incident-management/README.md +338 -0
- package/profiles/incident-management/profile.json +256 -0
- package/profiles/message-broker-management/README.md +344 -0
- package/profiles/message-broker-management/profile.json +399 -0
- package/profiles/profile-definition.schema.json +213 -0
- package/profiles/secrets-and-key-management/README.md +331 -0
- package/profiles/secrets-and-key-management/profile.json +219 -0
- package/schemas/v0.1/audit-event.schema.json +911 -0
- package/semantic-conventions/README.md +52 -0
- package/semantic-conventions/authentication.md +111 -0
- package/semantic-conventions/configuration-and-change.md +122 -0
- package/semantic-conventions/correlation-and-tracing.md +387 -0
- package/semantic-conventions/data-access.md +115 -0
- package/semantic-conventions/event-naming.md +114 -0
- package/semantic-conventions/identity-and-access.md +112 -0
- package/semantic-conventions/privileged-operations.md +110 -0
- package/semantic-conventions/workflow-and-approval.md +109 -0
- package/specification/actor-model.md +135 -0
- package/specification/approval-and-delegation.md +150 -0
- package/specification/authentication.md +116 -0
- package/specification/authorization.md +117 -0
- package/specification/change-model.md +134 -0
- package/specification/delivery.md +136 -0
- package/specification/design-principles.md +137 -0
- package/specification/event-model.md +387 -0
- package/specification/evidence-model.md +109 -0
- package/specification/extension-model.md +159 -0
- package/specification/integrity.md +314 -0
- package/specification/overview.md +172 -0
- package/specification/privacy.md +386 -0
- package/specification/resource-model.md +116 -0
- package/specification/terminology.md +135 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema validation with no filesystem dependency.
|
|
3
|
+
*
|
|
4
|
+
* This module is the boundary that lets the conformance engines run unchanged
|
|
5
|
+
* in environments that do not read the schema from disk — the MCP server in
|
|
6
|
+
* particular, where the schema is compiled in at build time.
|
|
7
|
+
*
|
|
8
|
+
* Everything here operates on a schema **object**. Locating and reading schema
|
|
9
|
+
* files lives in `validate.ts`, which imports this module.
|
|
10
|
+
*/
|
|
11
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
12
|
+
import formatsModule from "ajv-formats";
|
|
13
|
+
import { toIssues } from "./format-errors.js";
|
|
14
|
+
import { createValidatorFromCompiled, SCHEMA_ID, SPEC_VERSION, } from "./validator-interface.js";
|
|
15
|
+
// Re-exported so that existing import sites keep working. Code that only needs
|
|
16
|
+
// a precompiled validator should import ./validator-interface.js directly, which
|
|
17
|
+
// keeps Ajv out of its bundle entirely.
|
|
18
|
+
export { createValidatorFromCompiled, SCHEMA_ID, SPEC_VERSION };
|
|
19
|
+
/** `ajv-formats` is published as CommonJS; this is its ESM-interop entry point. */
|
|
20
|
+
const addFormats = formatsModule.default;
|
|
21
|
+
/** Creates an Ajv instance configured the way OpenAuditModel conformance requires. */
|
|
22
|
+
export function createAjv() {
|
|
23
|
+
const ajv = new Ajv2020({
|
|
24
|
+
// Reject schema authoring mistakes rather than silently ignoring them.
|
|
25
|
+
strict: true,
|
|
26
|
+
// Union types are used deliberately by the free-form JSON value definition.
|
|
27
|
+
allowUnionTypes: true,
|
|
28
|
+
// `strictRequired` is an Ajv-specific authoring lint that rejects the
|
|
29
|
+
// standard `if`/`then` + `required` idiom, which OpenAuditModel relies on
|
|
30
|
+
// for its conditional rules. The idiom is valid Draft 2020-12 and portable
|
|
31
|
+
// across validators, so this single check is disabled.
|
|
32
|
+
strictRequired: false,
|
|
33
|
+
// Report every problem in one pass so that a report is actionable.
|
|
34
|
+
allErrors: true,
|
|
35
|
+
});
|
|
36
|
+
addFormats(ajv);
|
|
37
|
+
return ajv;
|
|
38
|
+
}
|
|
39
|
+
/** Validates a schema document itself against the Draft 2020-12 meta-schema. */
|
|
40
|
+
export function validateSchemaDocument(schema) {
|
|
41
|
+
const ajv = createAjv();
|
|
42
|
+
const valid = ajv.validateSchema(schema);
|
|
43
|
+
return valid ? [] : toIssues(ajv.errors);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Compiles a schema object into a reusable validator.
|
|
47
|
+
*
|
|
48
|
+
* Used directly where the schema is already in memory. Environments that forbid
|
|
49
|
+
* dynamic code generation cannot call this — Ajv compiles with `new Function` —
|
|
50
|
+
* and supply a precompiled validation function instead; see
|
|
51
|
+
* {@link createValidatorFromCompiled}.
|
|
52
|
+
*/
|
|
53
|
+
export function createValidatorFromSchema(schema) {
|
|
54
|
+
const ajv = createAjv();
|
|
55
|
+
const compiled = ajv.compile(schema);
|
|
56
|
+
const schemaId = typeof schema["$id"] === "string" ? schema["$id"] : SCHEMA_ID;
|
|
57
|
+
return {
|
|
58
|
+
schemaId,
|
|
59
|
+
validateEvent: (event) => (compiled(event) ? [] : toIssues(compiled.errors)),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=validate-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-core.js","sourceRoot":"","sources":["../../../conformance/src/validate-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,OAAO,aAAa,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAwB,MAAM,oBAAoB,CAAC;AACpE,OAAO,EACL,2BAA2B,EAC3B,SAAS,EACT,YAAY,GAEb,MAAM,0BAA0B,CAAC;AAElC,+EAA+E;AAC/E,iFAAiF;AACjF,wCAAwC;AACxC,OAAO,EAAE,2BAA2B,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AAGhE,mFAAmF;AACnF,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,CAAC;AAEzC,sFAAsF;AACtF,MAAM,UAAU,SAAS;IACvB,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC;QACtB,uEAAuE;QACvE,MAAM,EAAE,IAAI;QACZ,4EAA4E;QAC5E,eAAe,EAAE,IAAI;QACrB,sEAAsE;QACtE,0EAA0E;QAC1E,2EAA2E;QAC3E,uDAAuD;QACvD,cAAc,EAAE,KAAK;QACrB,mEAAmE;QACnE,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IACH,UAAU,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,sBAAsB,CAAC,MAAuB;IAC5D,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,MAAM,KAAK,GAAG,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,MAA8B,CAAC,CAAC;AACnE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAuB;IAC/D,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,MAAM,QAAQ,GAAqB,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE/E,OAAO;QACL,QAAQ;QACR,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;KAC7E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AnySchemaObject } from "ajv";
|
|
2
|
+
import type { ValidationIssue } from "./format-errors.js";
|
|
3
|
+
import { expandInputPaths } from "./sources.js";
|
|
4
|
+
import { createAjv, SCHEMA_ID, SPEC_VERSION, validateSchemaDocument, type EventValidator } from "./validate-core.js";
|
|
5
|
+
export { createAjv, SCHEMA_ID, SPEC_VERSION, validateSchemaDocument };
|
|
6
|
+
export type { EventValidator };
|
|
7
|
+
export declare const SCHEMA_RELATIVE_PATH: string;
|
|
8
|
+
/** Outcome of validating a single file. */
|
|
9
|
+
export type FileStatus = "valid" | "invalid" | "unreadable";
|
|
10
|
+
export interface FileValidationResult {
|
|
11
|
+
readonly file: string;
|
|
12
|
+
readonly status: FileStatus;
|
|
13
|
+
readonly issues: readonly ValidationIssue[];
|
|
14
|
+
/** Set when the file could not be read or parsed as JSON. */
|
|
15
|
+
readonly error?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface Validator extends EventValidator {
|
|
18
|
+
readonly schemaPath: string;
|
|
19
|
+
/** Reads, parses and validates a single JSON file. */
|
|
20
|
+
validateFile(file: string): FileValidationResult;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Walks upwards from a starting directory until the canonical schema is found.
|
|
24
|
+
* This keeps the CLI working when run from `dist/`, from the repository root,
|
|
25
|
+
* or from an installed package directory.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveSchemaPath(startDir?: string): string;
|
|
28
|
+
/** Reads the canonical schema document from disk. */
|
|
29
|
+
export declare function loadSchema(schemaPath?: string): AnySchemaObject;
|
|
30
|
+
/** Compiles the canonical schema and returns a reusable validator. */
|
|
31
|
+
export declare function createValidator(schemaPath?: string): Validator;
|
|
32
|
+
/** Re-exported so that consumers of the validator keep a single import site. */
|
|
33
|
+
export { expandInputPaths };
|
|
34
|
+
export type { ValidationIssue };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation of audit events against the canonical OpenAuditModel schema, read
|
|
3
|
+
* from this repository.
|
|
4
|
+
*
|
|
5
|
+
* The filesystem-free half lives in `validate-core.ts`, which this module
|
|
6
|
+
* re-exports so that existing import sites keep working. Environments without a
|
|
7
|
+
* filesystem import that module directly.
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { expandInputPaths, readJsonFile } from "./sources.js";
|
|
13
|
+
import { createAjv, createValidatorFromSchema, SCHEMA_ID, SPEC_VERSION, validateSchemaDocument, } from "./validate-core.js";
|
|
14
|
+
export { createAjv, SCHEMA_ID, SPEC_VERSION, validateSchemaDocument };
|
|
15
|
+
export const SCHEMA_RELATIVE_PATH = path.join("schemas", `v${SPEC_VERSION}`, "audit-event.schema.json");
|
|
16
|
+
/**
|
|
17
|
+
* Walks upwards from a starting directory until the canonical schema is found.
|
|
18
|
+
* This keeps the CLI working when run from `dist/`, from the repository root,
|
|
19
|
+
* or from an installed package directory.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveSchemaPath(startDir) {
|
|
22
|
+
const from = startDir ?? path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
let dir = path.resolve(from);
|
|
24
|
+
for (let depth = 0; depth < 10; depth += 1) {
|
|
25
|
+
const candidate = path.join(dir, SCHEMA_RELATIVE_PATH);
|
|
26
|
+
if (existsSync(candidate)) {
|
|
27
|
+
return candidate;
|
|
28
|
+
}
|
|
29
|
+
const parent = path.dirname(dir);
|
|
30
|
+
if (parent === dir) {
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
dir = parent;
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Unable to locate ${SCHEMA_RELATIVE_PATH}. Searched upwards from ${path.resolve(from)}.`);
|
|
36
|
+
}
|
|
37
|
+
/** Reads the canonical schema document from disk. */
|
|
38
|
+
export function loadSchema(schemaPath) {
|
|
39
|
+
const resolved = schemaPath ?? resolveSchemaPath();
|
|
40
|
+
const raw = readFileSync(resolved, "utf8");
|
|
41
|
+
return JSON.parse(raw);
|
|
42
|
+
}
|
|
43
|
+
/** Compiles the canonical schema and returns a reusable validator. */
|
|
44
|
+
export function createValidator(schemaPath) {
|
|
45
|
+
const resolvedPath = schemaPath ?? resolveSchemaPath();
|
|
46
|
+
const schema = loadSchema(resolvedPath);
|
|
47
|
+
const core = createValidatorFromSchema(schema);
|
|
48
|
+
const { schemaId } = core;
|
|
49
|
+
const validateEvent = (event) => core.validateEvent(event);
|
|
50
|
+
const validateFile = (file) => {
|
|
51
|
+
const parsed = readJsonFile(file);
|
|
52
|
+
if (!parsed.ok) {
|
|
53
|
+
return { file, status: "unreadable", issues: [], error: parsed.error };
|
|
54
|
+
}
|
|
55
|
+
const issues = validateEvent(parsed.value);
|
|
56
|
+
return { file, status: issues.length === 0 ? "valid" : "invalid", issues };
|
|
57
|
+
};
|
|
58
|
+
return { schemaId, schemaPath: resolvedPath, validateEvent, validateFile };
|
|
59
|
+
}
|
|
60
|
+
/** Re-exported so that consumers of the validator keep a single import site. */
|
|
61
|
+
export { expandInputPaths };
|
|
62
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../conformance/src/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The validator interface, with no dependency on Ajv's runtime.
|
|
3
|
+
*
|
|
4
|
+
* This module exists so that an environment which validates with an
|
|
5
|
+
* ahead-of-time compiled function never pulls Ajv's compiler into its bundle.
|
|
6
|
+
* The MCP server validates with Ajv's standalone output, so importing the
|
|
7
|
+
* compiler there would ship a large module of dead code and put runtime code
|
|
8
|
+
* generation on a path that has no use for it.
|
|
9
|
+
*
|
|
10
|
+
* `ValidateFunction` is imported as a type only, so it is erased at build time.
|
|
11
|
+
*/
|
|
12
|
+
import type { ValidateFunction } from "ajv";
|
|
13
|
+
import { type ValidationIssue } from "./format-errors.js";
|
|
14
|
+
export declare const SPEC_VERSION = "0.1";
|
|
15
|
+
/** Canonical identifier of the OpenAuditModel Audit Event Schema. */
|
|
16
|
+
export declare const SCHEMA_ID = "https://openauditmodel.org/schemas/audit-event/0.1/schema.json";
|
|
17
|
+
/**
|
|
18
|
+
* Validates parsed events. The smallest interface the conformance engines need,
|
|
19
|
+
* so that they work with a schema read from disk or compiled in at build time.
|
|
20
|
+
*/
|
|
21
|
+
export interface EventValidator {
|
|
22
|
+
readonly schemaId: string;
|
|
23
|
+
/** Validates an already parsed event and returns the issues found. */
|
|
24
|
+
validateEvent(event: unknown): ValidationIssue[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Wraps an already compiled Ajv validation function.
|
|
28
|
+
*
|
|
29
|
+
* This is how the MCP server validates: Ajv's standalone code generator emits
|
|
30
|
+
* the same validation logic ahead of time, so the server's verdict is identical
|
|
31
|
+
* to the command line tool's by construction rather than by coincidence — and
|
|
32
|
+
* Ajv's compiler is not reachable from its module graph.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createValidatorFromCompiled(compiled: ValidateFunction, schemaId?: string): EventValidator;
|
|
35
|
+
export type { ValidationIssue };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { toIssues } from "./format-errors.js";
|
|
2
|
+
export const SPEC_VERSION = "0.1";
|
|
3
|
+
/** Canonical identifier of the OpenAuditModel Audit Event Schema. */
|
|
4
|
+
export const SCHEMA_ID = "https://openauditmodel.org/schemas/audit-event/0.1/schema.json";
|
|
5
|
+
/**
|
|
6
|
+
* Wraps an already compiled Ajv validation function.
|
|
7
|
+
*
|
|
8
|
+
* This is how the MCP server validates: Ajv's standalone code generator emits
|
|
9
|
+
* the same validation logic ahead of time, so the server's verdict is identical
|
|
10
|
+
* to the command line tool's by construction rather than by coincidence — and
|
|
11
|
+
* Ajv's compiler is not reachable from its module graph.
|
|
12
|
+
*/
|
|
13
|
+
export function createValidatorFromCompiled(compiled, schemaId = SCHEMA_ID) {
|
|
14
|
+
return {
|
|
15
|
+
schemaId,
|
|
16
|
+
validateEvent: (event) => (compiled(event) ? [] : toIssues(compiled.errors)),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=validator-interface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator-interface.js","sourceRoot":"","sources":["../../../conformance/src/validator-interface.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,QAAQ,EAAwB,MAAM,oBAAoB,CAAC;AAEpE,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC;AAElC,qEAAqE;AACrE,MAAM,CAAC,MAAM,SAAS,GAAG,gEAAgE,CAAC;AAY1F;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CACzC,QAA0B,EAC1B,WAAmB,SAAS;IAE5B,OAAO;QACL,QAAQ;QACR,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;KAC7E,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openauditmodel/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Conformance toolchain for OpenAuditModel: validate audit events against the canonical JSON Schema, verify tamper-evidence, lint for leaked secrets and check domain profiles. Offline and deterministic.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"audit",
|
|
7
|
+
"audit-event",
|
|
8
|
+
"audit-log",
|
|
9
|
+
"specification",
|
|
10
|
+
"json-schema",
|
|
11
|
+
"conformance",
|
|
12
|
+
"openauditmodel"
|
|
13
|
+
],
|
|
14
|
+
"license": "Apache-2.0",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=22.0.0"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"auditmodel": "dist/conformance/src/cli.js",
|
|
21
|
+
"openauditmodel": "dist/conformance/src/cli.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist/conformance/src",
|
|
25
|
+
"schemas",
|
|
26
|
+
"profiles",
|
|
27
|
+
"semantic-conventions",
|
|
28
|
+
"specification",
|
|
29
|
+
"LICENSE",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.json",
|
|
34
|
+
"clean": "node --eval \"import('node:fs').then((fs) => fs.rmSync('dist', { recursive: true, force: true }))\"",
|
|
35
|
+
"auditmodel": "node dist/conformance/src/cli.js",
|
|
36
|
+
"format": "prettier --write .",
|
|
37
|
+
"format:check": "prettier --check .",
|
|
38
|
+
"lint": "eslint .",
|
|
39
|
+
"schema:validate": "npm run build && node --test dist/conformance/tests/schema-validation.test.js",
|
|
40
|
+
"test": "npm run build && node --test \"dist/conformance/tests/*.test.js\" \"dist/mcp/tests/*.test.js\"",
|
|
41
|
+
"validate:valid": "npm run build && node dist/conformance/src/cli.js validate examples/valid",
|
|
42
|
+
"verify:integrity": "npm run build && node dist/conformance/src/cli.js verify-integrity examples/integrity/valid && node dist/conformance/src/cli.js verify-chain examples/integrity/valid/three-event-chain",
|
|
43
|
+
"lint:privacy": "npm run build && node dist/conformance/src/cli.js lint-privacy examples/valid examples/integrity/valid examples/privacy/clean examples/profiles/identity-and-access-management/valid examples/profiles/document-management/valid examples/profiles/incident-management/valid examples/profiles/message-broker-management/valid examples/profiles/deployment-and-change-management/valid examples/profiles/financial-transaction-management/valid examples/profiles/secrets-and-key-management/valid examples/profiles/customer-and-account-management/valid examples/profiles/backup-and-recovery/valid examples/profiles/api-and-integration-management/valid",
|
|
44
|
+
"check:profile": "npm run build && node dist/conformance/src/cli.js check-profile examples/profiles/identity-and-access-management/valid --profile identity-and-access-management && node dist/conformance/src/cli.js check-profile examples/profiles/document-management/valid --profile document-management && node dist/conformance/src/cli.js check-profile examples/profiles/incident-management/valid --profile incident-management && node dist/conformance/src/cli.js check-profile examples/profiles/message-broker-management/valid --profile message-broker-management && node dist/conformance/src/cli.js check-profile examples/profiles/deployment-and-change-management/valid --profile deployment-and-change-management && node dist/conformance/src/cli.js check-profile examples/profiles/financial-transaction-management/valid --profile financial-transaction-management && node dist/conformance/src/cli.js check-profile examples/profiles/secrets-and-key-management/valid --profile secrets-and-key-management && node dist/conformance/src/cli.js check-profile examples/profiles/customer-and-account-management/valid --profile customer-and-account-management && node dist/conformance/src/cli.js check-profile examples/profiles/backup-and-recovery/valid --profile backup-and-recovery && node dist/conformance/src/cli.js check-profile examples/profiles/api-and-integration-management/valid --profile api-and-integration-management",
|
|
45
|
+
"fixtures:integrity": "npm run build && node dist/conformance/tools/generate-integrity-fixtures.js && prettier --write examples/integrity",
|
|
46
|
+
"fixtures:check": "npm run build && node dist/conformance/tools/generate-integrity-fixtures.js --check",
|
|
47
|
+
"verify": "npm run format:check && npm run lint && npm run test && npm run validate:valid && npm run verify:integrity && npm run lint:privacy && npm run check:profile",
|
|
48
|
+
"mcp:generate": "npm run generate --workspace mcp",
|
|
49
|
+
"mcp:check-generated": "npm run generate:check --workspace mcp",
|
|
50
|
+
"mcp:start": "npm run build && node dist/mcp/src/index.js",
|
|
51
|
+
"prepare": "npm run build",
|
|
52
|
+
"prepack": "npm run build && node scripts/verify-package.mjs",
|
|
53
|
+
"package:verify": "node scripts/verify-package.mjs",
|
|
54
|
+
"package:smoke": "npm run build && node scripts/package-smoke-test.mjs",
|
|
55
|
+
"site:build": "node scripts/build-site.mjs",
|
|
56
|
+
"site:check": "node scripts/build-site.mjs --check"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"ajv": "^8.17.1",
|
|
60
|
+
"ajv-formats": "^3.0.1",
|
|
61
|
+
"canonicalize": "^3.0.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@eslint/js": "^10.0.1",
|
|
65
|
+
"@types/node": "^22.10.2",
|
|
66
|
+
"eslint": "^10.8.0",
|
|
67
|
+
"prettier": "^3.4.2",
|
|
68
|
+
"typescript": "^5.7.2",
|
|
69
|
+
"typescript-eslint": "^8.18.1"
|
|
70
|
+
},
|
|
71
|
+
"homepage": "https://openauditmodel.org",
|
|
72
|
+
"repository": {
|
|
73
|
+
"type": "git",
|
|
74
|
+
"url": "git+https://github.com/OpenAuditModel/OpenAuditModel.git"
|
|
75
|
+
},
|
|
76
|
+
"bugs": {
|
|
77
|
+
"url": "https://github.com/OpenAuditModel/OpenAuditModel/issues"
|
|
78
|
+
},
|
|
79
|
+
"workspaces": [
|
|
80
|
+
"mcp"
|
|
81
|
+
],
|
|
82
|
+
"publishConfig": {
|
|
83
|
+
"access": "public"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Profiles
|
|
2
|
+
|
|
3
|
+
**Specification version: 0.1 · Status: Experimental**
|
|
4
|
+
|
|
5
|
+
A **profile** is an optional, stricter set of requirements for a specific domain, layered on top of
|
|
6
|
+
the core model.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
auditmodel check-profile <path...> --profile identity-and-access-management
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## The invariant
|
|
13
|
+
|
|
14
|
+
**A profile only ever adds constraints.**
|
|
15
|
+
|
|
16
|
+
A profile MAY:
|
|
17
|
+
|
|
18
|
+
- Require fields the core model leaves OPTIONAL, for a specific class of events.
|
|
19
|
+
- Require a field to have a particular JSON type.
|
|
20
|
+
- Require a field to equal a particular scalar.
|
|
21
|
+
- Recommend fields, producing warnings that never fail conformance.
|
|
22
|
+
|
|
23
|
+
A profile MUST NOT:
|
|
24
|
+
|
|
25
|
+
- Relax, replace or reinterpret any core requirement.
|
|
26
|
+
- Add a top-level property to an audit event.
|
|
27
|
+
- Redefine the meaning of a core field.
|
|
28
|
+
- Introduce product-specific, company-specific, country-specific or regulation-specific fields.
|
|
29
|
+
|
|
30
|
+
**Every profile-conforming event is a core-conforming event.** The reverse does not hold.
|
|
31
|
+
|
|
32
|
+
This is enforced structurally rather than by review. The rule vocabulary has no keyword that could
|
|
33
|
+
remove a requirement — there is no `optionalPaths`, no `exemptPaths`, no `overrides` — and core
|
|
34
|
+
validation runs first, so an event that fails the core schema is reported as core-invalid with its
|
|
35
|
+
profile rules **not evaluated**. Both properties are covered by tests. See
|
|
36
|
+
[ADR 0008](../decisions/0008-declarative-profile-conformance.md).
|
|
37
|
+
|
|
38
|
+
## Status in v0.1
|
|
39
|
+
|
|
40
|
+
| Profile | Status |
|
|
41
|
+
| --------------------------------------------------------------------- | ------------------------- |
|
|
42
|
+
| [identity-and-access-management](identity-and-access-management/) | **Implemented**, 11 rules |
|
|
43
|
+
| [document-management](document-management/) | **Implemented**, 11 rules |
|
|
44
|
+
| [incident-management](incident-management/) | **Implemented**, 15 rules |
|
|
45
|
+
| [message-broker-management](message-broker-management/) | **Implemented**, 12 rules |
|
|
46
|
+
| [deployment-and-change-management](deployment-and-change-management/) | **Implemented**, 13 rules |
|
|
47
|
+
| [financial-transaction-management](financial-transaction-management/) | **Implemented**, 12 rules |
|
|
48
|
+
| [secrets-and-key-management](secrets-and-key-management/) | **Implemented**, 14 rules |
|
|
49
|
+
| [customer-and-account-management](customer-and-account-management/) | **Implemented**, 13 rules |
|
|
50
|
+
| [backup-and-recovery](backup-and-recovery/) | **Implemented**, 13 rules |
|
|
51
|
+
| [api-and-integration-management](api-and-integration-management/) | **Implemented**, 13 rules |
|
|
52
|
+
|
|
53
|
+
All ten profiles are enforceable and carry fixtures. There are no placeholder profiles left in v0.1.
|
|
54
|
+
|
|
55
|
+
## Profile definition format
|
|
56
|
+
|
|
57
|
+
A profile is a JSON document at `profiles/<name>/profile.json`, validated against
|
|
58
|
+
[profile-definition.schema.json](profile-definition.schema.json). That schema validates **profile
|
|
59
|
+
documents**; it is not part of the canonical audit event schema and never constrains an audit event.
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"profileVersion": "0.1",
|
|
64
|
+
"name": "identity-and-access-management",
|
|
65
|
+
"version": "0.1",
|
|
66
|
+
"status": "experimental",
|
|
67
|
+
"coreVersions": ["0.1"],
|
|
68
|
+
"title": "Identity and Access Management Profile",
|
|
69
|
+
"description": "Additional conformance requirements for identity and access management audit events.",
|
|
70
|
+
"rules": []
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`version` is the profile's own version and moves independently of the core specification.
|
|
75
|
+
`coreVersions` lists the core versions the profile applies to; an event declaring any other
|
|
76
|
+
`specVersion` is **not applicable** rather than in violation.
|
|
77
|
+
|
|
78
|
+
### Rules
|
|
79
|
+
|
|
80
|
+
Each rule has an `id` and a `description`, and MAY have a `rationale` and a `severity`.
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"id": "IAM-ROLE-002",
|
|
85
|
+
"description": "A privileged role change is approved and performed from a multi-factor authenticated session.",
|
|
86
|
+
"rationale": "Privileged access is the control that protects every other control.",
|
|
87
|
+
"severity": "error",
|
|
88
|
+
"events": ["identity.role.assign", "identity.role.revoke"],
|
|
89
|
+
"when": { "path": "/metadata/role/privileged", "equals": true },
|
|
90
|
+
"requiredPaths": ["/approval", "/authentication"],
|
|
91
|
+
"requiredValues": [{ "path": "/authentication/mfa", "equals": true }]
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Rule severities are `info`, `warning` and `error`. Only `error` fails conformance. These are the
|
|
96
|
+
profile vocabulary and are **unrelated** to the privacy linter's severities, which describe a
|
|
97
|
+
different kind of risk.
|
|
98
|
+
|
|
99
|
+
### Selectors
|
|
100
|
+
|
|
101
|
+
A rule MUST have at least one selector.
|
|
102
|
+
|
|
103
|
+
| Selector | Matches |
|
|
104
|
+
| --------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
105
|
+
| `events` | Exact event names |
|
|
106
|
+
| `eventPrefixes` | Event names starting with the prefix. A prefix MUST end with a dot, so that `identity.role.` cannot match `identity.roles-export` |
|
|
107
|
+
|
|
108
|
+
There is no regular expression selector. A profile is a published statement about which events it
|
|
109
|
+
governs, and a pattern language turns "does this rule apply to my event?" into a question only a tool
|
|
110
|
+
can answer.
|
|
111
|
+
|
|
112
|
+
### Requirements
|
|
113
|
+
|
|
114
|
+
| Keyword | Path syntax | Meaning |
|
|
115
|
+
| ------------------ | ---------------------------------------- | -------------------------------------------------------- |
|
|
116
|
+
| `requiredPaths` | Absolute JSON Pointer | The value must be **present** |
|
|
117
|
+
| `requiredMetadata` | JSON Pointer **relative to `/metadata`** | The value must be present and of a JSON type |
|
|
118
|
+
| `requiredValues` | Absolute JSON Pointer | The value must be present and strictly equal to a scalar |
|
|
119
|
+
| `recommendedPaths` | Absolute JSON Pointer | Absence produces a warning, never a failure |
|
|
120
|
+
|
|
121
|
+
`requiredMetadata` paths are relative because every one of them starts `/metadata`; `/role/id` means
|
|
122
|
+
`/metadata/role/id`. All other paths are absolute pointers into the event. Findings always report the
|
|
123
|
+
**full** pointer, so a report never needs the reader to know which convention a keyword used.
|
|
124
|
+
|
|
125
|
+
Supported metadata types: `string`, `number`, `integer`, `boolean`, `object`, `array`. `integer` is a
|
|
126
|
+
subset of `number`. Profiles do not embed schema fragments in v0.1.
|
|
127
|
+
|
|
128
|
+
### Presence
|
|
129
|
+
|
|
130
|
+
A required path is satisfied only when the value:
|
|
131
|
+
|
|
132
|
+
- exists, **and**
|
|
133
|
+
- is not `null`, **and**
|
|
134
|
+
- is not an empty string, **and**
|
|
135
|
+
- is not an empty array, **and**
|
|
136
|
+
- is not an empty object.
|
|
137
|
+
|
|
138
|
+
`false` and `0` **are** present. They are answers, not absences: a profile that could not require
|
|
139
|
+
`mfa: false` to be recorded would be unable to distinguish "not recorded" from "recorded as false".
|
|
140
|
+
|
|
141
|
+
This differs deliberately from the privacy linter's notion of a populated value, which excludes
|
|
142
|
+
booleans because a credential cannot be `true`.
|
|
143
|
+
|
|
144
|
+
### Conditional requirements
|
|
145
|
+
|
|
146
|
+
A rule MAY carry one `when` condition: one path compared for equality against one scalar.
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{ "when": { "path": "/metadata/role/privileged", "equals": true } }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
That is the entire conditional mechanism. There are no boolean combinations, no nesting, no
|
|
153
|
+
comparison operators other than equality, and no expression language.
|
|
154
|
+
|
|
155
|
+
Comparison is strict: `1` does not equal `true`, and `"true"` does not equal `true`. **When the
|
|
156
|
+
condition's path is absent, the condition does not hold** and the rule contributes nothing. The
|
|
157
|
+
alternative — treating an absent flag as possibly true — would fail every event that omitted a field
|
|
158
|
+
the rule was never meant to govern. Profiles requiring such a flag require it separately, through
|
|
159
|
+
`requiredMetadata`.
|
|
160
|
+
|
|
161
|
+
## Results
|
|
162
|
+
|
|
163
|
+
| Status | Meaning | Exit |
|
|
164
|
+
| ---------------- | ----------------------------------------------------------------- | ---- |
|
|
165
|
+
| `conforming` | At least one rule matched and every requirement was satisfied | 0 |
|
|
166
|
+
| `violations` | A rule matched and a requirement was not satisfied | 1 |
|
|
167
|
+
| `core-invalid` | The event fails the core schema; profile rules were not evaluated | 1 |
|
|
168
|
+
| `not-applicable` | No rule in the profile governs this event | 3 |
|
|
169
|
+
|
|
170
|
+
**`not-applicable` is not conformance.** An event no rule governs is reported as out of scope, never
|
|
171
|
+
as conforming, so that a pipeline cannot read "the profile said nothing" as "the profile was
|
|
172
|
+
satisfied". Exit code `3` exists for the same reason.
|
|
173
|
+
|
|
174
|
+
When a run mixes conforming and not-applicable events, the exit code is `0`. Exit `3` means nothing
|
|
175
|
+
checked was governed at all.
|
|
176
|
+
|
|
177
|
+
## What profiles are not
|
|
178
|
+
|
|
179
|
+
**Profiles are not regulatory mappings.** A profile requires audit fields. It cites no regulation,
|
|
180
|
+
article, control identifier or jurisdiction, and the profile definition schema gives it nowhere to put
|
|
181
|
+
one. Regulations are revised on their own schedules and interpreted differently by different auditors;
|
|
182
|
+
a profile that encoded one would be wrong when the regulation changed and useless everywhere it did
|
|
183
|
+
not apply.
|
|
184
|
+
|
|
185
|
+
**Profile conformance is not legal or regulatory compliance**, and MUST NOT be presented as evidence
|
|
186
|
+
of it. It is a statement that an event carries the fields a domain agreed it should carry.
|
|
187
|
+
|
|
188
|
+
**Profiles do not replace privacy linting.** A profile says which fields must be present; the privacy
|
|
189
|
+
linter says which values must not. They are complementary and independent: a profile-conforming event
|
|
190
|
+
can still carry a secret, and `auditmodel lint-privacy` is what looks for one. Every published
|
|
191
|
+
profile fixture is required by test to pass both.
|
|
192
|
+
|
|
193
|
+
**Profiles are not a policy engine.** OpenAuditModel records decisions; it does not evaluate them.
|
|
194
|
+
|
|
195
|
+
## Proposing a profile
|
|
196
|
+
|
|
197
|
+
Use the profile proposal issue template. A proposal must show that the domain has requirements the
|
|
198
|
+
core cannot express and that those requirements are shared across independent products — not one
|
|
199
|
+
product's field list wearing a domain's name. See [CONTRIBUTING.md](../CONTRIBUTING.md).
|
|
200
|
+
|
|
201
|
+
Adding a profile requires **no code**: a profile is a JSON document plus fixtures.
|