qa360 1.0.3 → 1.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 (108) hide show
  1. package/dist/commands/history.js +1 -1
  2. package/dist/commands/pack.js +1 -1
  3. package/dist/commands/run.d.ts +1 -1
  4. package/dist/commands/run.d.ts.map +1 -1
  5. package/dist/commands/run.js +1 -1
  6. package/dist/commands/secrets.js +1 -1
  7. package/dist/commands/serve.js +1 -1
  8. package/dist/commands/verify.js +1 -1
  9. package/dist/core/adapters/gitleaks-secrets.d.ts +115 -0
  10. package/dist/core/adapters/gitleaks-secrets.d.ts.map +1 -0
  11. package/dist/core/adapters/gitleaks-secrets.js +410 -0
  12. package/dist/core/adapters/k6-perf.d.ts +86 -0
  13. package/dist/core/adapters/k6-perf.d.ts.map +1 -0
  14. package/dist/core/adapters/k6-perf.js +398 -0
  15. package/dist/core/adapters/osv-deps.d.ts +124 -0
  16. package/dist/core/adapters/osv-deps.d.ts.map +1 -0
  17. package/dist/core/adapters/osv-deps.js +372 -0
  18. package/dist/core/adapters/playwright-api.d.ts +82 -0
  19. package/dist/core/adapters/playwright-api.d.ts.map +1 -0
  20. package/dist/core/adapters/playwright-api.js +252 -0
  21. package/dist/core/adapters/playwright-ui.d.ts +115 -0
  22. package/dist/core/adapters/playwright-ui.d.ts.map +1 -0
  23. package/dist/core/adapters/playwright-ui.js +346 -0
  24. package/dist/core/adapters/semgrep-sast.d.ts +100 -0
  25. package/dist/core/adapters/semgrep-sast.d.ts.map +1 -0
  26. package/dist/core/adapters/semgrep-sast.js +322 -0
  27. package/dist/core/adapters/zap-dast.d.ts +134 -0
  28. package/dist/core/adapters/zap-dast.d.ts.map +1 -0
  29. package/dist/core/adapters/zap-dast.js +424 -0
  30. package/dist/core/hooks/compose.d.ts +62 -0
  31. package/dist/core/hooks/compose.d.ts.map +1 -0
  32. package/dist/core/hooks/compose.js +225 -0
  33. package/dist/core/hooks/runner.d.ts +69 -0
  34. package/dist/core/hooks/runner.d.ts.map +1 -0
  35. package/dist/core/hooks/runner.js +303 -0
  36. package/dist/core/index.d.ts +74 -0
  37. package/dist/core/index.d.ts.map +1 -0
  38. package/dist/core/index.js +39 -0
  39. package/dist/core/pack/migrator.d.ts +52 -0
  40. package/dist/core/pack/migrator.d.ts.map +1 -0
  41. package/dist/core/pack/migrator.js +304 -0
  42. package/dist/core/pack/validator.d.ts +43 -0
  43. package/dist/core/pack/validator.d.ts.map +1 -0
  44. package/dist/core/pack/validator.js +292 -0
  45. package/dist/core/proof/bundle.d.ts +138 -0
  46. package/dist/core/proof/bundle.d.ts.map +1 -0
  47. package/dist/core/proof/bundle.js +160 -0
  48. package/dist/core/proof/canonicalize.d.ts +48 -0
  49. package/dist/core/proof/canonicalize.d.ts.map +1 -0
  50. package/dist/core/proof/canonicalize.js +105 -0
  51. package/dist/core/proof/index.d.ts +14 -0
  52. package/dist/core/proof/index.d.ts.map +1 -0
  53. package/dist/core/proof/index.js +18 -0
  54. package/dist/core/proof/schema.d.ts +218 -0
  55. package/dist/core/proof/schema.d.ts.map +1 -0
  56. package/dist/core/proof/schema.js +263 -0
  57. package/dist/core/proof/signer.d.ts +112 -0
  58. package/dist/core/proof/signer.d.ts.map +1 -0
  59. package/dist/core/proof/signer.js +226 -0
  60. package/dist/core/proof/verifier.d.ts +98 -0
  61. package/dist/core/proof/verifier.d.ts.map +1 -0
  62. package/dist/core/proof/verifier.js +302 -0
  63. package/dist/core/runner/phase3-runner.d.ts +102 -0
  64. package/dist/core/runner/phase3-runner.d.ts.map +1 -0
  65. package/dist/core/runner/phase3-runner.js +471 -0
  66. package/dist/core/secrets/crypto.d.ts +76 -0
  67. package/dist/core/secrets/crypto.d.ts.map +1 -0
  68. package/dist/core/secrets/crypto.js +225 -0
  69. package/dist/core/secrets/manager.d.ts +77 -0
  70. package/dist/core/secrets/manager.d.ts.map +1 -0
  71. package/dist/core/secrets/manager.js +219 -0
  72. package/dist/core/security/redaction-patterns-extended.d.ts +28 -0
  73. package/dist/core/security/redaction-patterns-extended.d.ts.map +1 -0
  74. package/dist/core/security/redaction-patterns-extended.js +247 -0
  75. package/dist/core/security/redactor.d.ts +72 -0
  76. package/dist/core/security/redactor.d.ts.map +1 -0
  77. package/dist/core/security/redactor.js +279 -0
  78. package/dist/core/serve/diagnostics-collector.d.ts +33 -0
  79. package/dist/core/serve/diagnostics-collector.d.ts.map +1 -0
  80. package/dist/core/serve/diagnostics-collector.js +149 -0
  81. package/dist/core/serve/health-checker.d.ts +45 -0
  82. package/dist/core/serve/health-checker.d.ts.map +1 -0
  83. package/dist/core/serve/health-checker.js +219 -0
  84. package/dist/core/serve/index.d.ts +9 -0
  85. package/dist/core/serve/index.d.ts.map +1 -0
  86. package/dist/core/serve/index.js +8 -0
  87. package/dist/core/serve/metrics-collector.d.ts +25 -0
  88. package/dist/core/serve/metrics-collector.d.ts.map +1 -0
  89. package/dist/core/serve/metrics-collector.js +322 -0
  90. package/dist/core/serve/process-manager.d.ts +37 -0
  91. package/dist/core/serve/process-manager.d.ts.map +1 -0
  92. package/dist/core/serve/process-manager.js +213 -0
  93. package/dist/core/serve/server.d.ts +37 -0
  94. package/dist/core/serve/server.d.ts.map +1 -0
  95. package/dist/core/serve/server.js +191 -0
  96. package/dist/core/types/pack-v1.d.ts +162 -0
  97. package/dist/core/types/pack-v1.d.ts.map +1 -0
  98. package/dist/core/types/pack-v1.js +5 -0
  99. package/dist/core/types/trust-score.d.ts +70 -0
  100. package/dist/core/types/trust-score.d.ts.map +1 -0
  101. package/dist/core/types/trust-score.js +191 -0
  102. package/dist/core/vault/cas.d.ts +87 -0
  103. package/dist/core/vault/cas.d.ts.map +1 -0
  104. package/dist/core/vault/cas.js +255 -0
  105. package/dist/core/vault/index.d.ts +205 -0
  106. package/dist/core/vault/index.d.ts.map +1 -0
  107. package/dist/core/vault/index.js +631 -0
  108. package/package.json +12 -6
@@ -0,0 +1,105 @@
1
+ /**
2
+ * JSON Canonicalization for deterministic hashing
3
+ *
4
+ * Implements RFC 8785-like canonicalization:
5
+ * - Alphabetically sorted keys (recursive)
6
+ * - UTF-8 NFC normalization
7
+ * - No whitespace (compact)
8
+ * - Deterministic across platforms
9
+ *
10
+ * @see docs/rfc/proof-bundle-v1.md#4-canonicalization
11
+ */
12
+ /**
13
+ * Canonicalize a JavaScript object into deterministic JSON string
14
+ *
15
+ * @param obj - Object to canonicalize
16
+ * @returns Canonical JSON string (compact, sorted keys, UTF-8 NFC)
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const obj = { b: 2, a: 1 };
21
+ * const canonical = canonicalize(obj);
22
+ * // Returns: '{"a":1,"b":2}\n'
23
+ * ```
24
+ */
25
+ export function canonicalize(obj) {
26
+ // Handle primitives
27
+ if (obj === null)
28
+ return 'null';
29
+ if (obj === undefined)
30
+ return 'null'; // Treat undefined as null
31
+ const type = typeof obj;
32
+ if (type === 'boolean')
33
+ return obj ? 'true' : 'false';
34
+ if (type === 'number') {
35
+ // Ensure decimal notation (no scientific notation)
36
+ if (!isFinite(obj)) {
37
+ throw new Error('Cannot canonicalize non-finite number');
38
+ }
39
+ return String(obj);
40
+ }
41
+ if (type === 'string') {
42
+ // Unicode NFC normalization
43
+ const normalized = obj.normalize('NFC');
44
+ // JSON.stringify handles escaping
45
+ return JSON.stringify(normalized);
46
+ }
47
+ // Handle arrays
48
+ if (Array.isArray(obj)) {
49
+ const items = obj.map(item => canonicalize(item));
50
+ return `[${items.join(',')}]`;
51
+ }
52
+ // Handle objects
53
+ if (type === 'object') {
54
+ // Get keys and sort alphabetically
55
+ const keys = Object.keys(obj).sort();
56
+ // Build key-value pairs
57
+ const pairs = keys.map(key => {
58
+ const value = obj[key];
59
+ const canonicalKey = canonicalize(key);
60
+ const canonicalValue = canonicalize(value);
61
+ return `${canonicalKey}:${canonicalValue}`;
62
+ });
63
+ return `{${pairs.join(',')}}`;
64
+ }
65
+ throw new Error(`Cannot canonicalize type: ${type}`);
66
+ }
67
+ /**
68
+ * Canonicalize and append newline (standard format)
69
+ *
70
+ * @param obj - Object to canonicalize
71
+ * @returns Canonical JSON string with trailing newline
72
+ */
73
+ export function canonicalizeWithNewline(obj) {
74
+ return canonicalize(obj) + '\n';
75
+ }
76
+ /**
77
+ * Remove signature field and canonicalize
78
+ * Used for signature verification
79
+ *
80
+ * @param bundle - Proof bundle (may contain signature field)
81
+ * @returns Canonical JSON without signature field
82
+ */
83
+ export function canonicalizeForSigning(bundle) {
84
+ // Clone to avoid mutating original
85
+ const clone = JSON.parse(JSON.stringify(bundle));
86
+ // Remove signature field
87
+ delete clone.signature;
88
+ return canonicalizeWithNewline(clone);
89
+ }
90
+ /**
91
+ * Verify canonicalization is deterministic
92
+ *
93
+ * @param obj - Object to test
94
+ * @returns true if canonicalization is stable
95
+ */
96
+ export function isCanonicalStable(obj) {
97
+ try {
98
+ const first = canonicalize(obj);
99
+ const second = canonicalize(obj);
100
+ return first === second;
101
+ }
102
+ catch {
103
+ return false;
104
+ }
105
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * QA360 Proof Bundle System
3
+ *
4
+ * Cryptographically signed, verifiable proof bundles for test results.
5
+ *
6
+ * @module @qa360/core/proof
7
+ * @see docs/rfc/proof-bundle-v1.md
8
+ */
9
+ export { canonicalize, canonicalizeWithNewline, canonicalizeForSigning, isCanonicalStable, } from './canonicalize.js';
10
+ export { generateKeys, saveKeys, loadKeys, keysExist, initializeKeys, ensureProofKeys, sha256, sign, verify, testRoundtrip, getKeyDirectory, getKeyPaths, type KeyPair, type KeyPaths, } from './signer.js';
11
+ export { validateProofBundle, getSchema, patterns, type ValidationResult, } from './schema.js';
12
+ export { createProofBundle, createProofBundleFromPack, computeSHA256, type ProofBundle, type RunMetadata, type Environment, type CIContext, type Artifact, type TestResults, type Gate, type SigningMetadata, type TimestampInfo, type IdentityInfo, type ProofBundleParams, } from './bundle.js';
13
+ export { verifyProofBundle, verifyArtifact, verifyArtifacts, verifyComplete, verifyProofFile, verifyPhase3Proof, VerificationCode, type VerificationResult, type VerificationDetails, type ArtifactVerificationOptions, } from './verifier.js';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/proof/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,YAAY,EACZ,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,cAAc,EACd,eAAe,EACf,MAAM,EACN,IAAI,EACJ,MAAM,EACN,aAAa,EACb,eAAe,EACf,WAAW,EACX,KAAK,OAAO,EACZ,KAAK,QAAQ,GACd,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,mBAAmB,EACnB,SAAS,EACT,QAAQ,EACR,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,aAAa,EACb,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,IAAI,EACT,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,GACjC,MAAM,eAAe,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * QA360 Proof Bundle System
3
+ *
4
+ * Cryptographically signed, verifiable proof bundles for test results.
5
+ *
6
+ * @module @qa360/core/proof
7
+ * @see docs/rfc/proof-bundle-v1.md
8
+ */
9
+ // Canonicalization
10
+ export { canonicalize, canonicalizeWithNewline, canonicalizeForSigning, isCanonicalStable, } from './canonicalize.js';
11
+ // Signing
12
+ export { generateKeys, saveKeys, loadKeys, keysExist, initializeKeys, ensureProofKeys, sha256, sign, verify, testRoundtrip, getKeyDirectory, getKeyPaths, } from './signer.js';
13
+ // Schema validation
14
+ export { validateProofBundle, getSchema, patterns, } from './schema.js';
15
+ // Bundle creation
16
+ export { createProofBundle, createProofBundleFromPack, computeSHA256, } from './bundle.js';
17
+ // Verification
18
+ export { verifyProofBundle, verifyArtifact, verifyArtifacts, verifyComplete, verifyProofFile, verifyPhase3Proof, VerificationCode, } from './verifier.js';
@@ -0,0 +1,218 @@
1
+ /**
2
+ * JSON Schema Validation for Proof Bundles
3
+ *
4
+ * Validates proof bundles against the v1 schema using AJV.
5
+ *
6
+ * @see docs/rfc/proof-bundle-v1.md#7-json-schema
7
+ */
8
+ /**
9
+ * Validation result
10
+ */
11
+ export interface ValidationResult {
12
+ valid: boolean;
13
+ errors?: string[];
14
+ }
15
+ /**
16
+ * Validate proof bundle against schema
17
+ *
18
+ * @param bundle - Proof bundle to validate
19
+ * @returns Validation result with errors if invalid
20
+ */
21
+ export declare function validateProofBundle(bundle: any): ValidationResult;
22
+ /**
23
+ * Get the JSON Schema object
24
+ *
25
+ * @returns Proof Bundle v1 schema
26
+ */
27
+ export declare function getSchema(): {
28
+ $schema: string;
29
+ $id: string;
30
+ title: string;
31
+ type: string;
32
+ required: string[];
33
+ additionalProperties: boolean;
34
+ properties: {
35
+ spec: {
36
+ type: string;
37
+ const: string;
38
+ };
39
+ run: {
40
+ type: string;
41
+ required: string[];
42
+ additionalProperties: boolean;
43
+ properties: {
44
+ id: {
45
+ type: string;
46
+ format: string;
47
+ };
48
+ startedAt: {
49
+ type: string;
50
+ format: string;
51
+ };
52
+ finishedAt: {
53
+ type: string;
54
+ format: string;
55
+ };
56
+ environment: {
57
+ type: string;
58
+ required: string[];
59
+ additionalProperties: boolean;
60
+ properties: {
61
+ os: {
62
+ type: string;
63
+ enum: string[];
64
+ };
65
+ node: {
66
+ type: string;
67
+ pattern: string;
68
+ };
69
+ arch: {
70
+ type: string;
71
+ enum: string[];
72
+ };
73
+ ci: {
74
+ type: string;
75
+ };
76
+ };
77
+ };
78
+ packHash: {
79
+ type: string;
80
+ pattern: string;
81
+ };
82
+ ciContext: {
83
+ type: string;
84
+ properties: {
85
+ provider: {
86
+ type: string[];
87
+ };
88
+ };
89
+ };
90
+ };
91
+ };
92
+ artifacts: {
93
+ type: string;
94
+ items: {
95
+ type: string;
96
+ required: string[];
97
+ additionalProperties: boolean;
98
+ properties: {
99
+ name: {
100
+ type: string;
101
+ minLength: number;
102
+ };
103
+ sha256: {
104
+ type: string;
105
+ pattern: string;
106
+ };
107
+ size: {
108
+ type: string;
109
+ minimum: number;
110
+ };
111
+ path: {
112
+ type: string;
113
+ };
114
+ };
115
+ };
116
+ };
117
+ results: {
118
+ type: string;
119
+ required: string[];
120
+ additionalProperties: boolean;
121
+ properties: {
122
+ trustScore: {
123
+ type: string;
124
+ minimum: number;
125
+ maximum: number;
126
+ };
127
+ gates: {
128
+ type: string;
129
+ items: {
130
+ type: string;
131
+ required: string[];
132
+ additionalProperties: boolean;
133
+ properties: {
134
+ name: {
135
+ type: string;
136
+ minLength: number;
137
+ };
138
+ status: {
139
+ type: string;
140
+ enum: string[];
141
+ };
142
+ metrics: {
143
+ type: string;
144
+ };
145
+ };
146
+ };
147
+ };
148
+ };
149
+ };
150
+ signing: {
151
+ type: string;
152
+ required: string[];
153
+ additionalProperties: boolean;
154
+ properties: {
155
+ algo: {
156
+ type: string;
157
+ const: string;
158
+ };
159
+ signerId: {
160
+ type: string;
161
+ minLength: number;
162
+ };
163
+ timestamp: {
164
+ type: string;
165
+ required: string[];
166
+ properties: {
167
+ type: {
168
+ type: string;
169
+ enum: string[];
170
+ };
171
+ token: {
172
+ type: string[];
173
+ };
174
+ };
175
+ };
176
+ identity: {
177
+ type: string;
178
+ required: string[];
179
+ properties: {
180
+ type: {
181
+ type: string;
182
+ enum: string[];
183
+ };
184
+ evidence: {
185
+ type: string[];
186
+ };
187
+ };
188
+ };
189
+ };
190
+ };
191
+ signature: {
192
+ type: string;
193
+ pattern: string;
194
+ };
195
+ };
196
+ };
197
+ /**
198
+ * Validate specific field patterns
199
+ */
200
+ export declare const patterns: {
201
+ /**
202
+ * Validate SHA-256 hash format
203
+ */
204
+ sha256: (hash: string) => boolean;
205
+ /**
206
+ * Validate UUID v4 format
207
+ */
208
+ uuid: (id: string) => boolean;
209
+ /**
210
+ * Validate Ed25519 signature format (base64, 88 chars)
211
+ */
212
+ signature: (sig: string) => boolean;
213
+ /**
214
+ * Validate Node.js version format
215
+ */
216
+ nodeVersion: (version: string) => boolean;
217
+ };
218
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/core/proof/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AA4MD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,GAAG,GAAG,gBAAgB,CAcjE;AAED;;;;GAIG;AACH,wBAAgB,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAExB;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ;IACnB;;OAEG;mBACY,MAAM,KAAG,OAAO;IAI/B;;OAEG;eACQ,MAAM,KAAG,OAAO;IAI3B;;OAEG;qBACc,MAAM,KAAG,OAAO;IAIjC;;OAEG;2BACoB,MAAM,KAAG,OAAO;CAGxC,CAAC"}
@@ -0,0 +1,263 @@
1
+ /**
2
+ * JSON Schema Validation for Proof Bundles
3
+ *
4
+ * Validates proof bundles against the v1 schema using AJV.
5
+ *
6
+ * @see docs/rfc/proof-bundle-v1.md#7-json-schema
7
+ */
8
+ import Ajv2020 from 'ajv/dist/2020.js';
9
+ import addFormats from 'ajv-formats';
10
+ /**
11
+ * Proof Bundle v1 JSON Schema (Draft 2020-12)
12
+ *
13
+ * Uses AJV 2020-12 for full JSON Schema 2020-12 support.
14
+ * This ensures compatibility with the RFC Proof Bundle v1 specification.
15
+ */
16
+ const PROOF_BUNDLE_SCHEMA = {
17
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
18
+ $id: 'https://qa360.dev/schemas/proof-bundle-v1.json',
19
+ title: 'QA360 Proof Bundle v1',
20
+ type: 'object',
21
+ required: ['spec', 'run', 'artifacts', 'results', 'signing', 'signature'],
22
+ additionalProperties: false,
23
+ properties: {
24
+ spec: {
25
+ type: 'string',
26
+ const: 'qa360.proof.v1',
27
+ },
28
+ run: {
29
+ type: 'object',
30
+ required: ['id', 'startedAt', 'finishedAt', 'environment', 'packHash'],
31
+ additionalProperties: false,
32
+ properties: {
33
+ id: {
34
+ type: 'string',
35
+ format: 'uuid',
36
+ },
37
+ startedAt: {
38
+ type: 'string',
39
+ format: 'date-time',
40
+ },
41
+ finishedAt: {
42
+ type: 'string',
43
+ format: 'date-time',
44
+ },
45
+ environment: {
46
+ type: 'object',
47
+ required: ['os', 'node', 'arch', 'ci'],
48
+ additionalProperties: false,
49
+ properties: {
50
+ os: {
51
+ type: 'string',
52
+ enum: ['windows', 'linux', 'darwin'],
53
+ },
54
+ node: {
55
+ type: 'string',
56
+ pattern: '^\\d+\\.\\d+\\.\\d+$',
57
+ },
58
+ arch: {
59
+ type: 'string',
60
+ enum: ['x64', 'arm64'],
61
+ },
62
+ ci: {
63
+ type: 'boolean',
64
+ },
65
+ },
66
+ },
67
+ packHash: {
68
+ type: 'string',
69
+ pattern: '^sha256-[0-9a-f]{64}$',
70
+ },
71
+ ciContext: {
72
+ type: 'object',
73
+ properties: {
74
+ provider: {
75
+ type: ['string', 'null'],
76
+ },
77
+ },
78
+ },
79
+ },
80
+ },
81
+ artifacts: {
82
+ type: 'array',
83
+ items: {
84
+ type: 'object',
85
+ required: ['name', 'sha256', 'size'],
86
+ additionalProperties: false,
87
+ properties: {
88
+ name: {
89
+ type: 'string',
90
+ minLength: 1,
91
+ },
92
+ sha256: {
93
+ type: 'string',
94
+ pattern: '^sha256-[0-9a-f]{64}$',
95
+ },
96
+ size: {
97
+ type: 'integer',
98
+ minimum: 0,
99
+ },
100
+ path: {
101
+ type: 'string',
102
+ },
103
+ },
104
+ },
105
+ },
106
+ results: {
107
+ type: 'object',
108
+ required: ['trustScore', 'gates'],
109
+ additionalProperties: false,
110
+ properties: {
111
+ trustScore: {
112
+ type: 'integer',
113
+ minimum: 0,
114
+ maximum: 100,
115
+ },
116
+ gates: {
117
+ type: 'array',
118
+ items: {
119
+ type: 'object',
120
+ required: ['name', 'status'],
121
+ additionalProperties: false,
122
+ properties: {
123
+ name: {
124
+ type: 'string',
125
+ minLength: 1,
126
+ },
127
+ status: {
128
+ type: 'string',
129
+ enum: ['pass', 'fail', 'skip'],
130
+ },
131
+ metrics: {
132
+ type: 'object',
133
+ },
134
+ },
135
+ },
136
+ },
137
+ },
138
+ },
139
+ signing: {
140
+ type: 'object',
141
+ required: ['algo', 'signerId', 'timestamp', 'identity'],
142
+ additionalProperties: false,
143
+ properties: {
144
+ algo: {
145
+ type: 'string',
146
+ const: 'ed25519',
147
+ },
148
+ signerId: {
149
+ type: 'string',
150
+ minLength: 1,
151
+ },
152
+ timestamp: {
153
+ type: 'object',
154
+ required: ['type'],
155
+ properties: {
156
+ type: {
157
+ type: 'string',
158
+ enum: ['none', 'rfc3161'],
159
+ },
160
+ token: {
161
+ type: ['string', 'null'],
162
+ },
163
+ },
164
+ },
165
+ identity: {
166
+ type: 'object',
167
+ required: ['type'],
168
+ properties: {
169
+ type: {
170
+ type: 'string',
171
+ enum: ['none', 'did', 'sigstore'],
172
+ },
173
+ evidence: {
174
+ type: ['string', 'object', 'null'],
175
+ },
176
+ },
177
+ },
178
+ },
179
+ },
180
+ signature: {
181
+ type: 'string',
182
+ pattern: '^[A-Za-z0-9+/]{86}==$',
183
+ },
184
+ },
185
+ };
186
+ /**
187
+ * Create AJV 2020-12 validator instance
188
+ *
189
+ * Configured with:
190
+ * - strict: true (enforce strict schema validation)
191
+ * - allErrors: true (collect all validation errors)
192
+ * - allowUnionTypes: true (support type unions like ['string', 'null'])
193
+ * - strictTypes: false (allow flexible type checking for edge cases)
194
+ */
195
+ let validator = null;
196
+ function getValidator() {
197
+ if (!validator) {
198
+ const ajv = new Ajv2020({
199
+ strict: true,
200
+ allErrors: true,
201
+ allowUnionTypes: true,
202
+ strictTypes: false,
203
+ });
204
+ addFormats(ajv);
205
+ validator = ajv.compile(PROOF_BUNDLE_SCHEMA);
206
+ }
207
+ return validator;
208
+ }
209
+ /**
210
+ * Validate proof bundle against schema
211
+ *
212
+ * @param bundle - Proof bundle to validate
213
+ * @returns Validation result with errors if invalid
214
+ */
215
+ export function validateProofBundle(bundle) {
216
+ const validate = getValidator();
217
+ const valid = validate(bundle);
218
+ if (valid) {
219
+ return { valid: true };
220
+ }
221
+ const errors = validate.errors?.map(err => {
222
+ const path = err.instancePath || 'root';
223
+ return `${path}: ${err.message}`;
224
+ }) || ['Unknown validation error'];
225
+ return { valid: false, errors };
226
+ }
227
+ /**
228
+ * Get the JSON Schema object
229
+ *
230
+ * @returns Proof Bundle v1 schema
231
+ */
232
+ export function getSchema() {
233
+ return PROOF_BUNDLE_SCHEMA;
234
+ }
235
+ /**
236
+ * Validate specific field patterns
237
+ */
238
+ export const patterns = {
239
+ /**
240
+ * Validate SHA-256 hash format
241
+ */
242
+ sha256: (hash) => {
243
+ return /^sha256-[0-9a-f]{64}$/.test(hash);
244
+ },
245
+ /**
246
+ * Validate UUID v4 format
247
+ */
248
+ uuid: (id) => {
249
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id);
250
+ },
251
+ /**
252
+ * Validate Ed25519 signature format (base64, 88 chars)
253
+ */
254
+ signature: (sig) => {
255
+ return /^[A-Za-z0-9+/]{86}==$/.test(sig);
256
+ },
257
+ /**
258
+ * Validate Node.js version format
259
+ */
260
+ nodeVersion: (version) => {
261
+ return /^\d+\.\d+\.\d+$/.test(version);
262
+ },
263
+ };