qa360 1.0.4 → 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.
- package/dist/commands/history.js +1 -1
- package/dist/commands/pack.js +1 -1
- package/dist/commands/run.d.ts +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +1 -1
- package/dist/commands/secrets.js +1 -1
- package/dist/commands/serve.js +1 -1
- package/dist/commands/verify.js +1 -1
- package/dist/core/adapters/gitleaks-secrets.d.ts +115 -0
- package/dist/core/adapters/gitleaks-secrets.d.ts.map +1 -0
- package/dist/core/adapters/gitleaks-secrets.js +410 -0
- package/dist/core/adapters/k6-perf.d.ts +86 -0
- package/dist/core/adapters/k6-perf.d.ts.map +1 -0
- package/dist/core/adapters/k6-perf.js +398 -0
- package/dist/core/adapters/osv-deps.d.ts +124 -0
- package/dist/core/adapters/osv-deps.d.ts.map +1 -0
- package/dist/core/adapters/osv-deps.js +372 -0
- package/dist/core/adapters/playwright-api.d.ts +82 -0
- package/dist/core/adapters/playwright-api.d.ts.map +1 -0
- package/dist/core/adapters/playwright-api.js +252 -0
- package/dist/core/adapters/playwright-ui.d.ts +115 -0
- package/dist/core/adapters/playwright-ui.d.ts.map +1 -0
- package/dist/core/adapters/playwright-ui.js +346 -0
- package/dist/core/adapters/semgrep-sast.d.ts +100 -0
- package/dist/core/adapters/semgrep-sast.d.ts.map +1 -0
- package/dist/core/adapters/semgrep-sast.js +322 -0
- package/dist/core/adapters/zap-dast.d.ts +134 -0
- package/dist/core/adapters/zap-dast.d.ts.map +1 -0
- package/dist/core/adapters/zap-dast.js +424 -0
- package/dist/core/hooks/compose.d.ts +62 -0
- package/dist/core/hooks/compose.d.ts.map +1 -0
- package/dist/core/hooks/compose.js +225 -0
- package/dist/core/hooks/runner.d.ts +69 -0
- package/dist/core/hooks/runner.d.ts.map +1 -0
- package/dist/core/hooks/runner.js +303 -0
- package/dist/core/index.d.ts +74 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +39 -0
- package/dist/core/pack/migrator.d.ts +52 -0
- package/dist/core/pack/migrator.d.ts.map +1 -0
- package/dist/core/pack/migrator.js +304 -0
- package/dist/core/pack/validator.d.ts +43 -0
- package/dist/core/pack/validator.d.ts.map +1 -0
- package/dist/core/pack/validator.js +292 -0
- package/dist/core/proof/bundle.d.ts +138 -0
- package/dist/core/proof/bundle.d.ts.map +1 -0
- package/dist/core/proof/bundle.js +160 -0
- package/dist/core/proof/canonicalize.d.ts +48 -0
- package/dist/core/proof/canonicalize.d.ts.map +1 -0
- package/dist/core/proof/canonicalize.js +105 -0
- package/dist/core/proof/index.d.ts +14 -0
- package/dist/core/proof/index.d.ts.map +1 -0
- package/dist/core/proof/index.js +18 -0
- package/dist/core/proof/schema.d.ts +218 -0
- package/dist/core/proof/schema.d.ts.map +1 -0
- package/dist/core/proof/schema.js +263 -0
- package/dist/core/proof/signer.d.ts +112 -0
- package/dist/core/proof/signer.d.ts.map +1 -0
- package/dist/core/proof/signer.js +226 -0
- package/dist/core/proof/verifier.d.ts +98 -0
- package/dist/core/proof/verifier.d.ts.map +1 -0
- package/dist/core/proof/verifier.js +302 -0
- package/dist/core/runner/phase3-runner.d.ts +102 -0
- package/dist/core/runner/phase3-runner.d.ts.map +1 -0
- package/dist/core/runner/phase3-runner.js +471 -0
- package/dist/core/secrets/crypto.d.ts +76 -0
- package/dist/core/secrets/crypto.d.ts.map +1 -0
- package/dist/core/secrets/crypto.js +225 -0
- package/dist/core/secrets/manager.d.ts +77 -0
- package/dist/core/secrets/manager.d.ts.map +1 -0
- package/dist/core/secrets/manager.js +219 -0
- package/dist/core/security/redaction-patterns-extended.d.ts +28 -0
- package/dist/core/security/redaction-patterns-extended.d.ts.map +1 -0
- package/dist/core/security/redaction-patterns-extended.js +247 -0
- package/dist/core/security/redactor.d.ts +72 -0
- package/dist/core/security/redactor.d.ts.map +1 -0
- package/dist/core/security/redactor.js +279 -0
- package/dist/core/serve/diagnostics-collector.d.ts +33 -0
- package/dist/core/serve/diagnostics-collector.d.ts.map +1 -0
- package/dist/core/serve/diagnostics-collector.js +149 -0
- package/dist/core/serve/health-checker.d.ts +45 -0
- package/dist/core/serve/health-checker.d.ts.map +1 -0
- package/dist/core/serve/health-checker.js +219 -0
- package/dist/core/serve/index.d.ts +9 -0
- package/dist/core/serve/index.d.ts.map +1 -0
- package/dist/core/serve/index.js +8 -0
- package/dist/core/serve/metrics-collector.d.ts +25 -0
- package/dist/core/serve/metrics-collector.d.ts.map +1 -0
- package/dist/core/serve/metrics-collector.js +322 -0
- package/dist/core/serve/process-manager.d.ts +37 -0
- package/dist/core/serve/process-manager.d.ts.map +1 -0
- package/dist/core/serve/process-manager.js +213 -0
- package/dist/core/serve/server.d.ts +37 -0
- package/dist/core/serve/server.d.ts.map +1 -0
- package/dist/core/serve/server.js +191 -0
- package/dist/core/types/pack-v1.d.ts +162 -0
- package/dist/core/types/pack-v1.d.ts.map +1 -0
- package/dist/core/types/pack-v1.js +5 -0
- package/dist/core/types/trust-score.d.ts +70 -0
- package/dist/core/types/trust-score.d.ts.map +1 -0
- package/dist/core/types/trust-score.js +191 -0
- package/dist/core/vault/cas.d.ts +87 -0
- package/dist/core/vault/cas.d.ts.map +1 -0
- package/dist/core/vault/cas.js +255 -0
- package/dist/core/vault/index.d.ts +205 -0
- package/dist/core/vault/index.d.ts.map +1 -0
- package/dist/core/vault/index.js +631 -0
- package/package.json +12 -6
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Content-Addressable Storage (CAS)
|
|
3
|
+
* Stores files by their SHA256 hash with deduplication
|
|
4
|
+
*/
|
|
5
|
+
export interface CASArtifact {
|
|
6
|
+
sha256: string;
|
|
7
|
+
path: string;
|
|
8
|
+
size: number;
|
|
9
|
+
exists: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface CASStats {
|
|
12
|
+
totalFiles: number;
|
|
13
|
+
totalBytes: number;
|
|
14
|
+
orphanedFiles: number;
|
|
15
|
+
orphanedBytes: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class ContentAddressableStorage {
|
|
18
|
+
private readonly casRoot;
|
|
19
|
+
constructor(baseDir: string);
|
|
20
|
+
/**
|
|
21
|
+
* Save content to CAS, returns artifact metadata
|
|
22
|
+
*/
|
|
23
|
+
saveArtifact(content: Buffer, mimeType: string, originalName?: string): Promise<CASArtifact>;
|
|
24
|
+
/**
|
|
25
|
+
* Save file to CAS by path
|
|
26
|
+
*/
|
|
27
|
+
saveFile(filePath: string, mimeType: string): Promise<CASArtifact>;
|
|
28
|
+
/**
|
|
29
|
+
* Retrieve content from CAS
|
|
30
|
+
*/
|
|
31
|
+
getArtifact(sha256: string): Promise<Buffer | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Check if artifact exists in CAS
|
|
34
|
+
*/
|
|
35
|
+
exists(sha256: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Get artifact metadata
|
|
38
|
+
*/
|
|
39
|
+
getArtifactInfo(sha256: string): Promise<{
|
|
40
|
+
size: number;
|
|
41
|
+
mtime: Date;
|
|
42
|
+
} | null>;
|
|
43
|
+
/**
|
|
44
|
+
* List all artifacts in CAS
|
|
45
|
+
*/
|
|
46
|
+
listArtifacts(): Promise<string[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Get CAS statistics
|
|
49
|
+
*/
|
|
50
|
+
getStats(referencedHashes?: Set<string>): Promise<CASStats>;
|
|
51
|
+
/**
|
|
52
|
+
* Remove orphaned artifacts
|
|
53
|
+
*/
|
|
54
|
+
cleanupOrphans(referencedHashes: Set<string>): Promise<{
|
|
55
|
+
removedFiles: number;
|
|
56
|
+
freedBytes: number;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* Calculate SHA256 hash of content
|
|
60
|
+
*/
|
|
61
|
+
private calculateHash;
|
|
62
|
+
/**
|
|
63
|
+
* Get CAS file path for a given hash (with sharding)
|
|
64
|
+
*/
|
|
65
|
+
private getCASPath;
|
|
66
|
+
/**
|
|
67
|
+
* Ensure CAS directory structure exists
|
|
68
|
+
*/
|
|
69
|
+
private ensureCASDirectory;
|
|
70
|
+
/**
|
|
71
|
+
* Clean up empty shard directories
|
|
72
|
+
*/
|
|
73
|
+
private cleanupEmptyDirectories;
|
|
74
|
+
/**
|
|
75
|
+
* Verify artifact integrity
|
|
76
|
+
*/
|
|
77
|
+
verifyArtifact(sha256: string): Promise<boolean>;
|
|
78
|
+
/**
|
|
79
|
+
* Copy artifact to external path
|
|
80
|
+
*/
|
|
81
|
+
exportArtifact(sha256: string, targetPath: string): Promise<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Get human-readable size
|
|
84
|
+
*/
|
|
85
|
+
static formatBytes(bytes: number): string;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=cas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cas.d.ts","sourceRoot":"","sources":["../../../src/core/vault/cas.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,MAAM;IAK3B;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IA+BlG;;OAEG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAMxE;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUzD;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAI/B;;OAEG;IACG,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAcpF;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAgCxC;;OAEG;IACG,QAAQ,CAAC,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;IA4BjE;;OAEG;IACG,cAAc,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IA0B1G;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;OAEG;IACH,OAAO,CAAC,UAAU;IAYlB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;OAEG;YACW,uBAAuB;IAgCrC;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUtD;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAW1E;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAY1C"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Content-Addressable Storage (CAS)
|
|
3
|
+
* Stores files by their SHA256 hash with deduplication
|
|
4
|
+
*/
|
|
5
|
+
import { createHash } from 'crypto';
|
|
6
|
+
import { promises as fs } from 'fs';
|
|
7
|
+
import { join, dirname } from 'path';
|
|
8
|
+
import { existsSync, mkdirSync, statSync } from 'fs';
|
|
9
|
+
export class ContentAddressableStorage {
|
|
10
|
+
casRoot;
|
|
11
|
+
constructor(baseDir) {
|
|
12
|
+
this.casRoot = join(baseDir, 'cas');
|
|
13
|
+
this.ensureCASDirectory();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Save content to CAS, returns artifact metadata
|
|
17
|
+
*/
|
|
18
|
+
async saveArtifact(content, mimeType, originalName) {
|
|
19
|
+
const sha256 = this.calculateHash(content);
|
|
20
|
+
const casPath = this.getCASPath(sha256);
|
|
21
|
+
const size = content.length;
|
|
22
|
+
// Check if file already exists (deduplication)
|
|
23
|
+
if (existsSync(casPath)) {
|
|
24
|
+
return {
|
|
25
|
+
sha256,
|
|
26
|
+
path: casPath,
|
|
27
|
+
size,
|
|
28
|
+
exists: true
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// Ensure directory exists
|
|
32
|
+
await fs.mkdir(dirname(casPath), { recursive: true });
|
|
33
|
+
// Write content atomically
|
|
34
|
+
const tempPath = `${casPath}.tmp`;
|
|
35
|
+
await fs.writeFile(tempPath, content);
|
|
36
|
+
await fs.rename(tempPath, casPath);
|
|
37
|
+
return {
|
|
38
|
+
sha256,
|
|
39
|
+
path: casPath,
|
|
40
|
+
size,
|
|
41
|
+
exists: false
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Save file to CAS by path
|
|
46
|
+
*/
|
|
47
|
+
async saveFile(filePath, mimeType) {
|
|
48
|
+
const content = await fs.readFile(filePath);
|
|
49
|
+
const originalName = filePath.split('/').pop();
|
|
50
|
+
return this.saveArtifact(content, mimeType, originalName);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Retrieve content from CAS
|
|
54
|
+
*/
|
|
55
|
+
async getArtifact(sha256) {
|
|
56
|
+
const casPath = this.getCASPath(sha256);
|
|
57
|
+
if (!existsSync(casPath)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return fs.readFile(casPath);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if artifact exists in CAS
|
|
64
|
+
*/
|
|
65
|
+
exists(sha256) {
|
|
66
|
+
return existsSync(this.getCASPath(sha256));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get artifact metadata
|
|
70
|
+
*/
|
|
71
|
+
async getArtifactInfo(sha256) {
|
|
72
|
+
const casPath = this.getCASPath(sha256);
|
|
73
|
+
if (!existsSync(casPath)) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const stats = statSync(casPath);
|
|
77
|
+
return {
|
|
78
|
+
size: stats.size,
|
|
79
|
+
mtime: stats.mtime
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* List all artifacts in CAS
|
|
84
|
+
*/
|
|
85
|
+
async listArtifacts() {
|
|
86
|
+
const artifacts = [];
|
|
87
|
+
try {
|
|
88
|
+
const shards = await fs.readdir(this.casRoot);
|
|
89
|
+
for (const shard of shards) {
|
|
90
|
+
if (shard.length !== 2)
|
|
91
|
+
continue;
|
|
92
|
+
const shardPath = join(this.casRoot, shard);
|
|
93
|
+
const subShards = await fs.readdir(shardPath);
|
|
94
|
+
for (const subShard of subShards) {
|
|
95
|
+
if (subShard.length !== 2)
|
|
96
|
+
continue;
|
|
97
|
+
const subShardPath = join(shardPath, subShard);
|
|
98
|
+
const files = await fs.readdir(subShardPath);
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
if (file.length === 64) { // SHA256 length
|
|
101
|
+
artifacts.push(file);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
// CAS directory might not exist yet
|
|
109
|
+
}
|
|
110
|
+
return artifacts;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get CAS statistics
|
|
114
|
+
*/
|
|
115
|
+
async getStats(referencedHashes) {
|
|
116
|
+
const artifacts = await this.listArtifacts();
|
|
117
|
+
let totalFiles = 0;
|
|
118
|
+
let totalBytes = 0;
|
|
119
|
+
let orphanedFiles = 0;
|
|
120
|
+
let orphanedBytes = 0;
|
|
121
|
+
for (const sha256 of artifacts) {
|
|
122
|
+
const info = await this.getArtifactInfo(sha256);
|
|
123
|
+
if (!info)
|
|
124
|
+
continue;
|
|
125
|
+
totalFiles++;
|
|
126
|
+
totalBytes += info.size;
|
|
127
|
+
if (referencedHashes && !referencedHashes.has(sha256)) {
|
|
128
|
+
orphanedFiles++;
|
|
129
|
+
orphanedBytes += info.size;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
totalFiles,
|
|
134
|
+
totalBytes,
|
|
135
|
+
orphanedFiles,
|
|
136
|
+
orphanedBytes
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Remove orphaned artifacts
|
|
141
|
+
*/
|
|
142
|
+
async cleanupOrphans(referencedHashes) {
|
|
143
|
+
const artifacts = await this.listArtifacts();
|
|
144
|
+
let removedFiles = 0;
|
|
145
|
+
let freedBytes = 0;
|
|
146
|
+
for (const sha256 of artifacts) {
|
|
147
|
+
if (referencedHashes.has(sha256)) {
|
|
148
|
+
continue; // Keep referenced artifacts
|
|
149
|
+
}
|
|
150
|
+
const casPath = this.getCASPath(sha256);
|
|
151
|
+
const info = await this.getArtifactInfo(sha256);
|
|
152
|
+
if (info) {
|
|
153
|
+
await fs.unlink(casPath);
|
|
154
|
+
removedFiles++;
|
|
155
|
+
freedBytes += info.size;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Clean up empty directories
|
|
159
|
+
await this.cleanupEmptyDirectories();
|
|
160
|
+
return { removedFiles, freedBytes };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Calculate SHA256 hash of content
|
|
164
|
+
*/
|
|
165
|
+
calculateHash(content) {
|
|
166
|
+
return createHash('sha256').update(content).digest('hex');
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get CAS file path for a given hash (with sharding)
|
|
170
|
+
*/
|
|
171
|
+
getCASPath(sha256) {
|
|
172
|
+
if (sha256.length !== 64) {
|
|
173
|
+
throw new Error(`Invalid SHA256 hash: ${sha256}`);
|
|
174
|
+
}
|
|
175
|
+
// Shard using first 4 characters: aa/bb/aabbcc...
|
|
176
|
+
const shard1 = sha256.substring(0, 2);
|
|
177
|
+
const shard2 = sha256.substring(2, 4);
|
|
178
|
+
return join(this.casRoot, shard1, shard2, sha256);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Ensure CAS directory structure exists
|
|
182
|
+
*/
|
|
183
|
+
ensureCASDirectory() {
|
|
184
|
+
if (!existsSync(this.casRoot)) {
|
|
185
|
+
mkdirSync(this.casRoot, { recursive: true });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Clean up empty shard directories
|
|
190
|
+
*/
|
|
191
|
+
async cleanupEmptyDirectories() {
|
|
192
|
+
try {
|
|
193
|
+
const shards = await fs.readdir(this.casRoot);
|
|
194
|
+
for (const shard of shards) {
|
|
195
|
+
if (shard.length !== 2)
|
|
196
|
+
continue;
|
|
197
|
+
const shardPath = join(this.casRoot, shard);
|
|
198
|
+
const subShards = await fs.readdir(shardPath);
|
|
199
|
+
for (const subShard of subShards) {
|
|
200
|
+
if (subShard.length !== 2)
|
|
201
|
+
continue;
|
|
202
|
+
const subShardPath = join(shardPath, subShard);
|
|
203
|
+
const files = await fs.readdir(subShardPath);
|
|
204
|
+
if (files.length === 0) {
|
|
205
|
+
await fs.rmdir(subShardPath);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Check if shard directory is now empty
|
|
209
|
+
const remainingSubShards = await fs.readdir(shardPath);
|
|
210
|
+
if (remainingSubShards.length === 0) {
|
|
211
|
+
await fs.rmdir(shardPath);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
// Ignore cleanup errors
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Verify artifact integrity
|
|
221
|
+
*/
|
|
222
|
+
async verifyArtifact(sha256) {
|
|
223
|
+
const content = await this.getArtifact(sha256);
|
|
224
|
+
if (!content) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
const actualHash = this.calculateHash(content);
|
|
228
|
+
return actualHash === sha256;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Copy artifact to external path
|
|
232
|
+
*/
|
|
233
|
+
async exportArtifact(sha256, targetPath) {
|
|
234
|
+
const content = await this.getArtifact(sha256);
|
|
235
|
+
if (!content) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
await fs.mkdir(dirname(targetPath), { recursive: true });
|
|
239
|
+
await fs.writeFile(targetPath, content);
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get human-readable size
|
|
244
|
+
*/
|
|
245
|
+
static formatBytes(bytes) {
|
|
246
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
247
|
+
let size = bytes;
|
|
248
|
+
let unitIndex = 0;
|
|
249
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
250
|
+
size /= 1024;
|
|
251
|
+
unitIndex++;
|
|
252
|
+
}
|
|
253
|
+
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Evidence Vault Engine
|
|
3
|
+
* SQLite WAL-based storage with idempotence and CAS integration
|
|
4
|
+
*/
|
|
5
|
+
import { CASArtifact } from './cas.js';
|
|
6
|
+
export interface VaultConfig {
|
|
7
|
+
baseDir: string;
|
|
8
|
+
enableWAL?: boolean;
|
|
9
|
+
maxConnections?: number;
|
|
10
|
+
retentionDays?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface RunRecord {
|
|
13
|
+
id: string;
|
|
14
|
+
run_key?: string;
|
|
15
|
+
started_at: number;
|
|
16
|
+
ended_at?: number;
|
|
17
|
+
pack_hash: string;
|
|
18
|
+
pack_path: string;
|
|
19
|
+
status: 'running' | 'passed' | 'failed' | 'cancelled' | 'error';
|
|
20
|
+
trust_score?: number;
|
|
21
|
+
weights_json?: string;
|
|
22
|
+
sign_alg?: string;
|
|
23
|
+
signature_hex?: string;
|
|
24
|
+
proof_pdf_path?: string;
|
|
25
|
+
pinned: number;
|
|
26
|
+
created_at: number;
|
|
27
|
+
updated_at: number;
|
|
28
|
+
}
|
|
29
|
+
export interface GateRecord {
|
|
30
|
+
id?: number;
|
|
31
|
+
run_id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
status: 'passed' | 'failed' | 'skipped' | 'warn';
|
|
34
|
+
duration_ms?: number;
|
|
35
|
+
metrics_json?: string;
|
|
36
|
+
budgets_json?: string;
|
|
37
|
+
started_at?: number;
|
|
38
|
+
ended_at?: number;
|
|
39
|
+
created_at: number;
|
|
40
|
+
}
|
|
41
|
+
export interface FindingRecord {
|
|
42
|
+
id?: number;
|
|
43
|
+
run_id: string;
|
|
44
|
+
gate: string;
|
|
45
|
+
severity: 'info' | 'low' | 'medium' | 'high' | 'critical';
|
|
46
|
+
rule: string;
|
|
47
|
+
location?: string;
|
|
48
|
+
message: string;
|
|
49
|
+
raw_output?: string;
|
|
50
|
+
fingerprint?: string;
|
|
51
|
+
created_at: number;
|
|
52
|
+
}
|
|
53
|
+
export interface ArtifactRecord {
|
|
54
|
+
sha256: string;
|
|
55
|
+
mime_type: string;
|
|
56
|
+
size_bytes: number;
|
|
57
|
+
cas_path: string;
|
|
58
|
+
original_name?: string;
|
|
59
|
+
created_at: number;
|
|
60
|
+
last_accessed: number;
|
|
61
|
+
}
|
|
62
|
+
export interface RunArtifactLink {
|
|
63
|
+
run_id: string;
|
|
64
|
+
sha256: string;
|
|
65
|
+
label: string;
|
|
66
|
+
created_at: number;
|
|
67
|
+
}
|
|
68
|
+
export interface BeginRunOptions {
|
|
69
|
+
run_key?: string;
|
|
70
|
+
pack_path: string;
|
|
71
|
+
pack_hash: string;
|
|
72
|
+
weights?: Record<string, any>;
|
|
73
|
+
}
|
|
74
|
+
export interface FinishRunOptions {
|
|
75
|
+
status: 'passed' | 'failed' | 'cancelled' | 'error';
|
|
76
|
+
trust_score?: number;
|
|
77
|
+
signature?: string;
|
|
78
|
+
proof_pdf_sha?: string;
|
|
79
|
+
weights?: Record<string, any>;
|
|
80
|
+
}
|
|
81
|
+
export interface RunFilters {
|
|
82
|
+
status?: string[];
|
|
83
|
+
since?: Date;
|
|
84
|
+
until?: Date;
|
|
85
|
+
pack_hash?: string;
|
|
86
|
+
limit?: number;
|
|
87
|
+
offset?: number;
|
|
88
|
+
}
|
|
89
|
+
export interface VaultStats {
|
|
90
|
+
totalRuns: number;
|
|
91
|
+
totalGates: number;
|
|
92
|
+
totalFindings: number;
|
|
93
|
+
totalArtifacts: number;
|
|
94
|
+
vaultSizeBytes: number;
|
|
95
|
+
oldestRun?: Date;
|
|
96
|
+
newestRun?: Date;
|
|
97
|
+
}
|
|
98
|
+
export declare class EvidenceVault {
|
|
99
|
+
private db;
|
|
100
|
+
private cas;
|
|
101
|
+
private redactor;
|
|
102
|
+
private readonly dbPath;
|
|
103
|
+
private readonly baseDir;
|
|
104
|
+
constructor(config: VaultConfig);
|
|
105
|
+
/**
|
|
106
|
+
* Open vault connection and initialize schema
|
|
107
|
+
*/
|
|
108
|
+
static open(baseDir: string, config?: Partial<VaultConfig>): Promise<EvidenceVault>;
|
|
109
|
+
/**
|
|
110
|
+
* Begin a new run with idempotence support
|
|
111
|
+
*/
|
|
112
|
+
beginRun(options: BeginRunOptions): Promise<{
|
|
113
|
+
runId: string;
|
|
114
|
+
isReused: boolean;
|
|
115
|
+
}>;
|
|
116
|
+
/**
|
|
117
|
+
* Finish a run with final status and signature
|
|
118
|
+
*/
|
|
119
|
+
finishRun(runId: string, options: FinishRunOptions): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* Record gate execution result
|
|
122
|
+
*/
|
|
123
|
+
recordGate(runId: string, gate: Omit<GateRecord, 'id' | 'run_id' | 'created_at'>): Promise<number>;
|
|
124
|
+
/**
|
|
125
|
+
* Record security/quality finding
|
|
126
|
+
*/
|
|
127
|
+
recordFinding(runId: string, finding: Omit<FindingRecord, 'id' | 'run_id' | 'created_at'>): Promise<number>;
|
|
128
|
+
/**
|
|
129
|
+
* Store artifact in CAS and link to run
|
|
130
|
+
*/
|
|
131
|
+
storeArtifact(runId: string, content: Buffer, mimeType: string, label: string, originalName?: string): Promise<CASArtifact>;
|
|
132
|
+
/**
|
|
133
|
+
* Attach existing artifact to run
|
|
134
|
+
*/
|
|
135
|
+
attachArtifact(runId: string, link: {
|
|
136
|
+
sha256: string;
|
|
137
|
+
label: string;
|
|
138
|
+
}): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* Get run by ID
|
|
141
|
+
*/
|
|
142
|
+
getRun(runId: string): Promise<RunRecord | null>;
|
|
143
|
+
/**
|
|
144
|
+
* Get run by run_key
|
|
145
|
+
*/
|
|
146
|
+
getRunByKey(runKey: string): Promise<RunRecord | null>;
|
|
147
|
+
/**
|
|
148
|
+
* List runs with filters
|
|
149
|
+
*/
|
|
150
|
+
listRuns(filters?: RunFilters): Promise<RunRecord[]>;
|
|
151
|
+
/**
|
|
152
|
+
* Get gates for a run
|
|
153
|
+
*/
|
|
154
|
+
getGates(runId: string): Promise<GateRecord[]>;
|
|
155
|
+
/**
|
|
156
|
+
* Get findings for a run
|
|
157
|
+
*/
|
|
158
|
+
getFindings(runId: string, gate?: string): Promise<FindingRecord[]>;
|
|
159
|
+
/**
|
|
160
|
+
* Get artifacts for a run
|
|
161
|
+
*/
|
|
162
|
+
getRunArtifacts(runId: string): Promise<Array<ArtifactRecord & {
|
|
163
|
+
label: string;
|
|
164
|
+
}>>;
|
|
165
|
+
/**
|
|
166
|
+
* Get artifact by SHA256
|
|
167
|
+
*/
|
|
168
|
+
getArtifact(sha256: string): Promise<ArtifactRecord | null>;
|
|
169
|
+
/**
|
|
170
|
+
* Pin/unpin run to protect from GC
|
|
171
|
+
*/
|
|
172
|
+
pinRun(runId: string, pinned?: boolean): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* Calculate pack hash for idempotence
|
|
175
|
+
*/
|
|
176
|
+
static calculatePackHash(packContent: string): string;
|
|
177
|
+
/**
|
|
178
|
+
* Get vault statistics
|
|
179
|
+
*/
|
|
180
|
+
getStats(): Promise<VaultStats>;
|
|
181
|
+
private insertRun;
|
|
182
|
+
private updateRun;
|
|
183
|
+
private insertGate;
|
|
184
|
+
private insertFinding;
|
|
185
|
+
private insertArtifact;
|
|
186
|
+
private insertRunArtifact;
|
|
187
|
+
private generateFindingFingerprint;
|
|
188
|
+
/**
|
|
189
|
+
* Initialize database connection
|
|
190
|
+
*/
|
|
191
|
+
private initializeDatabase;
|
|
192
|
+
/**
|
|
193
|
+
* Initialize database schema
|
|
194
|
+
*/
|
|
195
|
+
initializeSchema(): Promise<void>;
|
|
196
|
+
/**
|
|
197
|
+
* Ensure required directories exist
|
|
198
|
+
*/
|
|
199
|
+
private ensureDirectories;
|
|
200
|
+
/**
|
|
201
|
+
* Close database connection
|
|
202
|
+
*/
|
|
203
|
+
close(): Promise<void>;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/vault/index.ts"],"names":[],"mappings":"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"}
|