@algovoi/audit-verifier 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 +21 -0
- package/README.md +113 -0
- package/dist/canonicalize.d.ts +5 -0
- package/dist/canonicalize.d.ts.map +1 -0
- package/dist/canonicalize.js +26 -0
- package/dist/canonicalize.js.map +1 -0
- package/dist/check-report.d.ts +18 -0
- package/dist/check-report.d.ts.map +1 -0
- package/dist/check-report.js +48 -0
- package/dist/check-report.js.map +1 -0
- package/dist/checks.d.ts +9 -0
- package/dist/checks.d.ts.map +1 -0
- package/dist/checks.js +337 -0
- package/dist/checks.js.map +1 -0
- package/dist/demo.d.ts +17 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +186 -0
- package/dist/demo.js.map +1 -0
- package/dist/extractors.d.ts +13 -0
- package/dist/extractors.d.ts.map +1 -0
- package/dist/extractors.js +72 -0
- package/dist/extractors.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +59 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/verify.d.ts +10 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +44 -0
- package/dist/verify.js.map +1 -0
- package/package.json +64 -0
- package/src/canonicalize.ts +29 -0
- package/src/check-report.ts +58 -0
- package/src/checks.ts +435 -0
- package/src/demo.ts +214 -0
- package/src/extractors.ts +87 -0
- package/src/index.ts +44 -0
- package/src/types.ts +70 -0
- package/src/verify.ts +66 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-chain canonical-field extractors.
|
|
3
|
+
*
|
|
4
|
+
* Each chain (audit_log / screening_hits / compliance_events /
|
|
5
|
+
* negotiation_trace_events) commits a different set of fields to its
|
|
6
|
+
* content_hash. These functions MUST byte-for-byte match the Python
|
|
7
|
+
* sibling (verify_audit_bundle.py) and the emitter side, otherwise every
|
|
8
|
+
* row will fail verification on the auditor side.
|
|
9
|
+
*/
|
|
10
|
+
import type { AuditRow, FieldExtractor } from './types.js';
|
|
11
|
+
|
|
12
|
+
function auditLogCanonicalFields(row: AuditRow): Record<string, unknown> {
|
|
13
|
+
return {
|
|
14
|
+
trace_id: row.trace_id ?? null,
|
|
15
|
+
actor: row.actor ?? null,
|
|
16
|
+
action: row.action ?? null,
|
|
17
|
+
target_type: row.target_type ?? null,
|
|
18
|
+
target_id: row.target_id ?? null,
|
|
19
|
+
tenant_id: row.tenant_id ?? null,
|
|
20
|
+
before_state: row.before_state ?? null,
|
|
21
|
+
after_state: row.after_state ?? null,
|
|
22
|
+
ip_address: row.ip_address ?? null,
|
|
23
|
+
user_agent: row.user_agent ?? null,
|
|
24
|
+
created_at: row.created_at ?? null,
|
|
25
|
+
chain_position: row.chain_position ?? null,
|
|
26
|
+
prev_hash: row.prev_hash ?? null,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function screeningHitCanonicalFields(row: AuditRow): Record<string, unknown> {
|
|
31
|
+
return {
|
|
32
|
+
screened_at: row.screened_at ?? null,
|
|
33
|
+
subject_type: row.subject_type ?? null,
|
|
34
|
+
wallet_address: row.wallet_address ?? null,
|
|
35
|
+
tenant_id: row.tenant_id ?? null,
|
|
36
|
+
payment_ledger_id: row.payment_ledger_id ?? null,
|
|
37
|
+
sanctions_entry_id: row.sanctions_entry_id ?? null,
|
|
38
|
+
action_taken: row.action_taken ?? null,
|
|
39
|
+
screening_context: row.screening_context ?? null,
|
|
40
|
+
chain_position: row.chain_position ?? null,
|
|
41
|
+
prev_hash: row.prev_hash ?? null,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function complianceEventCanonicalFields(row: AuditRow): Record<string, unknown> {
|
|
46
|
+
return {
|
|
47
|
+
id: row.id ?? null,
|
|
48
|
+
tenant_id: row.tenant_id ?? null,
|
|
49
|
+
rule_id: row.rule_id ?? null,
|
|
50
|
+
payment_ledger_id: row.payment_ledger_id ?? null,
|
|
51
|
+
payer_address_snapshot: row.payer_address_snapshot ?? null,
|
|
52
|
+
review_of_event_id: row.review_of_event_id ?? null,
|
|
53
|
+
event_type: row.event_type ?? null,
|
|
54
|
+
metric_value: row.metric_value ?? null,
|
|
55
|
+
threshold_value: row.threshold_value ?? null,
|
|
56
|
+
created_at: row.created_at ?? null,
|
|
57
|
+
chain_position: row.chain_position ?? null,
|
|
58
|
+
prev_hash: row.prev_hash ?? null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function negotiationTraceCanonicalFields(row: AuditRow): Record<string, unknown> {
|
|
63
|
+
return {
|
|
64
|
+
trace_id: row.trace_id ?? null,
|
|
65
|
+
session_id: row.session_id ?? null,
|
|
66
|
+
tenant_id: row.tenant_id ?? null,
|
|
67
|
+
counterparty_a: row.counterparty_a ?? null,
|
|
68
|
+
counterparty_b: row.counterparty_b ?? null,
|
|
69
|
+
protocol: row.protocol ?? null,
|
|
70
|
+
message_seq: row.message_seq ?? null,
|
|
71
|
+
message_role: row.message_role ?? null,
|
|
72
|
+
message_payload: row.message_payload ?? null,
|
|
73
|
+
payment_ledger_id: row.payment_ledger_id ?? null,
|
|
74
|
+
created_at: row.created_at ?? null,
|
|
75
|
+
chain_position: row.chain_position ?? null,
|
|
76
|
+
prev_hash: row.prev_hash ?? null,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const FIELD_EXTRACTORS: Record<string, FieldExtractor> = {
|
|
81
|
+
audit_log: auditLogCanonicalFields,
|
|
82
|
+
screening_hits: screeningHitCanonicalFields,
|
|
83
|
+
compliance_events: complianceEventCanonicalFields,
|
|
84
|
+
negotiation_trace_events: negotiationTraceCanonicalFields,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const KNOWN_CHAIN_NAMES = Object.keys(FIELD_EXTRACTORS).sort();
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @algovoi/audit-verifier
|
|
3
|
+
*
|
|
4
|
+
* TypeScript reference verifier for AlgoVoi selective-disclosure audit
|
|
5
|
+
* bundles. Byte-for-byte parity with the Python sibling
|
|
6
|
+
* algovoi-audit-verifier (PyPI).
|
|
7
|
+
*
|
|
8
|
+
* The hosted version is at https://verify.algovoi.co.uk; the offline /
|
|
9
|
+
* embedded version is this package.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
canonicalize,
|
|
14
|
+
canonicaliseToBytes,
|
|
15
|
+
sha256Hex,
|
|
16
|
+
sha256JcsHex,
|
|
17
|
+
} from './canonicalize.js';
|
|
18
|
+
|
|
19
|
+
export { FIELD_EXTRACTORS, KNOWN_CHAIN_NAMES } from './extractors.js';
|
|
20
|
+
export { CheckReport } from './check-report.js';
|
|
21
|
+
export {
|
|
22
|
+
checkPerRowContentHash,
|
|
23
|
+
checkContinuity,
|
|
24
|
+
checkBundleSignature,
|
|
25
|
+
checkSelectionCriteriaMatch,
|
|
26
|
+
checkOffVmAnchor,
|
|
27
|
+
parseObjectKeyShaPrefix,
|
|
28
|
+
} from './checks.js';
|
|
29
|
+
export { verifyBundle, SUPPORTED_CHAIN_FORMAT_VERSIONS } from './verify.js';
|
|
30
|
+
export { buildDemoBundle } from './demo.js';
|
|
31
|
+
|
|
32
|
+
export type {
|
|
33
|
+
AuditBundle,
|
|
34
|
+
AuditRow,
|
|
35
|
+
BundleSignature,
|
|
36
|
+
ChainAnchor,
|
|
37
|
+
CheckEntry,
|
|
38
|
+
CheckReportJSON,
|
|
39
|
+
CheckResult,
|
|
40
|
+
FieldExtractor,
|
|
41
|
+
OffVmAnchor,
|
|
42
|
+
SelectionCriteria,
|
|
43
|
+
VerifyBundleOptions,
|
|
44
|
+
} from './types.js';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type surface for the audit-verifier.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type AuditRow = Record<string, unknown>;
|
|
6
|
+
|
|
7
|
+
export type FieldExtractor = (row: AuditRow) => Record<string, unknown>;
|
|
8
|
+
|
|
9
|
+
export interface BundleSignature {
|
|
10
|
+
hex: string;
|
|
11
|
+
algorithm?: string;
|
|
12
|
+
canonicalisation?: string;
|
|
13
|
+
key_id?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface OffVmAnchor {
|
|
17
|
+
bucket_name?: string;
|
|
18
|
+
object_key?: string;
|
|
19
|
+
region?: string;
|
|
20
|
+
object_lock_until?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ChainAnchor {
|
|
24
|
+
current_head?: {
|
|
25
|
+
chain_position?: number;
|
|
26
|
+
content_hash?: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SelectionCriteria {
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
since?: string;
|
|
33
|
+
until?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface BridgingMeta {
|
|
37
|
+
included?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface AuditBundle {
|
|
41
|
+
chain_format_version?: number;
|
|
42
|
+
chain_name?: string;
|
|
43
|
+
rows?: AuditRow[];
|
|
44
|
+
bridging_rows?: AuditRow[];
|
|
45
|
+
bridging?: BridgingMeta;
|
|
46
|
+
bundle_signature?: BundleSignature | null;
|
|
47
|
+
off_vm_anchor?: OffVmAnchor;
|
|
48
|
+
chain_anchor?: ChainAnchor;
|
|
49
|
+
selection_criteria?: SelectionCriteria;
|
|
50
|
+
[key: string]: unknown;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type CheckResult = 'ok' | 'fail' | 'skip';
|
|
54
|
+
|
|
55
|
+
export interface CheckEntry {
|
|
56
|
+
name: string;
|
|
57
|
+
passed: boolean | null; // true=ok, false=fail, null=skip
|
|
58
|
+
detail: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface VerifyBundleOptions {
|
|
62
|
+
signingKey?: string | null;
|
|
63
|
+
manifestDir?: string | null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface CheckReportJSON {
|
|
67
|
+
all_passed: boolean;
|
|
68
|
+
fatal: string[];
|
|
69
|
+
checks: CheckEntry[];
|
|
70
|
+
}
|
package/src/verify.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verifyBundle -- run all checks against a parsed bundle, return CheckReport.
|
|
3
|
+
*
|
|
4
|
+
* Behaviour byte-for-byte parity with the Python sibling verify_bundle().
|
|
5
|
+
*/
|
|
6
|
+
import { CheckReport } from './check-report.js';
|
|
7
|
+
import {
|
|
8
|
+
checkPerRowContentHash,
|
|
9
|
+
checkContinuity,
|
|
10
|
+
checkBundleSignature,
|
|
11
|
+
checkSelectionCriteriaMatch,
|
|
12
|
+
checkOffVmAnchor,
|
|
13
|
+
} from './checks.js';
|
|
14
|
+
import { KNOWN_CHAIN_NAMES } from './extractors.js';
|
|
15
|
+
import type { AuditBundle, VerifyBundleOptions } from './types.js';
|
|
16
|
+
|
|
17
|
+
export const SUPPORTED_CHAIN_FORMAT_VERSIONS = new Set<number>([1]);
|
|
18
|
+
|
|
19
|
+
export async function verifyBundle(
|
|
20
|
+
bundle: AuditBundle,
|
|
21
|
+
options: VerifyBundleOptions = {},
|
|
22
|
+
): Promise<CheckReport> {
|
|
23
|
+
const report = new CheckReport();
|
|
24
|
+
|
|
25
|
+
// Version check first.
|
|
26
|
+
const version = bundle.chain_format_version;
|
|
27
|
+
if (version === undefined || version === null) {
|
|
28
|
+
report.addFatal(
|
|
29
|
+
"bundle is missing required field 'chain_format_version' " +
|
|
30
|
+
'-- bundle is malformed or was not emitted by AlgoVoi',
|
|
31
|
+
);
|
|
32
|
+
return report;
|
|
33
|
+
}
|
|
34
|
+
if (!SUPPORTED_CHAIN_FORMAT_VERSIONS.has(version)) {
|
|
35
|
+
const supported = Array.from(SUPPORTED_CHAIN_FORMAT_VERSIONS).sort();
|
|
36
|
+
report.addFatal(
|
|
37
|
+
`bundle.chain_format_version=${JSON.stringify(version)} is not supported by ` +
|
|
38
|
+
`this verifier (supported: ${JSON.stringify(supported)}). ` +
|
|
39
|
+
'Pull a fresh verifier from https://github.com/chopmob-cloud/algovoi-audit-verifier ' +
|
|
40
|
+
'-- running an older verifier against a newer bundle risks a false PASS.',
|
|
41
|
+
);
|
|
42
|
+
return report;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const chainName = bundle.chain_name;
|
|
46
|
+
if (!chainName || !KNOWN_CHAIN_NAMES.includes(chainName)) {
|
|
47
|
+
report.addFatal(
|
|
48
|
+
`bundle.chain_name='${chainName}' is not recognised ` +
|
|
49
|
+
`(expected one of: ${JSON.stringify(KNOWN_CHAIN_NAMES)})`,
|
|
50
|
+
);
|
|
51
|
+
return report;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!('rows' in bundle)) {
|
|
55
|
+
report.addFatal("bundle is missing required field 'rows'");
|
|
56
|
+
return report;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
checkPerRowContentHash(bundle, report);
|
|
60
|
+
checkContinuity(bundle, report);
|
|
61
|
+
checkSelectionCriteriaMatch(bundle, report);
|
|
62
|
+
checkBundleSignature(bundle, options.signingKey ?? null, report);
|
|
63
|
+
await checkOffVmAnchor(bundle, options.manifestDir ?? null, report);
|
|
64
|
+
|
|
65
|
+
return report;
|
|
66
|
+
}
|