qa360 1.0.4 → 1.1.1
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 +13 -6
package/dist/commands/history.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from 'commander';
|
|
|
6
6
|
import { existsSync, createWriteStream } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
-
import { EvidenceVault } from '
|
|
9
|
+
import { EvidenceVault } from '../core/index.js';
|
|
10
10
|
export class QA360History {
|
|
11
11
|
vault;
|
|
12
12
|
constructor() { }
|
package/dist/commands/pack.js
CHANGED
|
@@ -7,7 +7,7 @@ import { join } from 'path';
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import ora from 'ora';
|
|
9
9
|
import * as yaml from 'js-yaml';
|
|
10
|
-
import { PackValidator, PackMigrator } from '
|
|
10
|
+
import { PackValidator, PackMigrator } from '../core/index.js';
|
|
11
11
|
export class QA360Pack {
|
|
12
12
|
qa360Dir = join(process.cwd(), '.qa360');
|
|
13
13
|
packPath = join(this.qa360Dir, 'pack.yml');
|
package/dist/commands/run.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAAgB,KAAK,eAAe,EAAkC,KAAK,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAAgB,KAAK,eAAe,EAAkC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEzH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAwBtE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BrD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CA8B5D;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAiDf;AAGD,eAAe,UAAU,CAAC"}
|
package/dist/commands/run.js
CHANGED
|
@@ -10,7 +10,7 @@ import { existsSync, readFileSync } from 'fs';
|
|
|
10
10
|
import { join, resolve } from 'path';
|
|
11
11
|
import chalk from 'chalk';
|
|
12
12
|
import { load } from 'js-yaml';
|
|
13
|
-
import { Phase3Runner, PackValidator } from '
|
|
13
|
+
import { Phase3Runner, PackValidator } from '../core/index.js';
|
|
14
14
|
/**
|
|
15
15
|
* Load and validate pack configuration
|
|
16
16
|
*/
|
package/dist/commands/secrets.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import ora from 'ora';
|
|
7
7
|
import inquirer from 'inquirer';
|
|
8
|
-
import { SecretsManager, SecretsCrypto } from '
|
|
8
|
+
import { SecretsManager, SecretsCrypto } from '../core/index.js';
|
|
9
9
|
export class QA360Secrets {
|
|
10
10
|
manager;
|
|
11
11
|
constructor() {
|
package/dist/commands/serve.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Démarre le serveur d'observabilité
|
|
4
4
|
*/
|
|
5
5
|
import { Command } from 'commander';
|
|
6
|
-
import { QA360Server } from '
|
|
6
|
+
import { QA360Server } from '../core/index.js';
|
|
7
7
|
export function createServeCommand() {
|
|
8
8
|
return new Command('serve')
|
|
9
9
|
.description('Start QA360 observability server')
|
package/dist/commands/verify.js
CHANGED
|
@@ -14,7 +14,7 @@ import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
|
14
14
|
import { join, resolve, basename } from 'path';
|
|
15
15
|
import chalk from 'chalk';
|
|
16
16
|
// Import from core
|
|
17
|
-
import { verifyProofFile, verifyPhase3Proof, VerificationCode, } from '
|
|
17
|
+
import { verifyProofFile, verifyPhase3Proof, VerificationCode, } from '../core/index.js';
|
|
18
18
|
/**
|
|
19
19
|
* Format verification result for human-readable output
|
|
20
20
|
*/
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Gitleaks Secrets Adapter (Sécurité Réelle)
|
|
3
|
+
* Scan for hardcoded secrets in source code
|
|
4
|
+
*/
|
|
5
|
+
import { PackSecurity } from '../types/pack-v1.js';
|
|
6
|
+
export interface GitleaksConfig {
|
|
7
|
+
workingDir: string;
|
|
8
|
+
security?: PackSecurity;
|
|
9
|
+
timeout?: number;
|
|
10
|
+
excludePaths?: string[];
|
|
11
|
+
configFile?: string;
|
|
12
|
+
verbose?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface GitleaksFinding {
|
|
15
|
+
Description: string;
|
|
16
|
+
StartLine: number;
|
|
17
|
+
EndLine: number;
|
|
18
|
+
StartColumn: number;
|
|
19
|
+
EndColumn: number;
|
|
20
|
+
Match: string;
|
|
21
|
+
Secret: string;
|
|
22
|
+
File: string;
|
|
23
|
+
SymlinkFile: string;
|
|
24
|
+
Commit: string;
|
|
25
|
+
Entropy: number;
|
|
26
|
+
Author: string;
|
|
27
|
+
Email: string;
|
|
28
|
+
Date: string;
|
|
29
|
+
Message: string;
|
|
30
|
+
Tags: string[];
|
|
31
|
+
RuleID: string;
|
|
32
|
+
Fingerprint: string;
|
|
33
|
+
}
|
|
34
|
+
export interface GitleaksScanResult {
|
|
35
|
+
success: boolean;
|
|
36
|
+
findings: GitleaksFinding[];
|
|
37
|
+
summary: {
|
|
38
|
+
total: number;
|
|
39
|
+
by_rule: Record<string, number>;
|
|
40
|
+
by_file: Record<string, number>;
|
|
41
|
+
high_entropy: number;
|
|
42
|
+
};
|
|
43
|
+
budgetCheck: {
|
|
44
|
+
findings_passed: boolean;
|
|
45
|
+
};
|
|
46
|
+
scannedPath: string;
|
|
47
|
+
error?: string;
|
|
48
|
+
rawOutput?: string;
|
|
49
|
+
junit?: string;
|
|
50
|
+
errorCode?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare class GitleaksSecretsAdapter {
|
|
53
|
+
private redactor;
|
|
54
|
+
private workingDir;
|
|
55
|
+
constructor(workingDir?: string);
|
|
56
|
+
/**
|
|
57
|
+
* Execute Gitleaks secrets scan
|
|
58
|
+
*/
|
|
59
|
+
runSecretsScan(config: GitleaksConfig): Promise<GitleaksScanResult>;
|
|
60
|
+
/**
|
|
61
|
+
* Prepare gitleaks configuration
|
|
62
|
+
*/
|
|
63
|
+
private prepareConfig;
|
|
64
|
+
/**
|
|
65
|
+
* Generate TOML config from object
|
|
66
|
+
*/
|
|
67
|
+
private generateTomlConfig;
|
|
68
|
+
/**
|
|
69
|
+
* Execute gitleaks scanner
|
|
70
|
+
*/
|
|
71
|
+
private executeGitleaks;
|
|
72
|
+
/**
|
|
73
|
+
* Parse gitleaks results
|
|
74
|
+
*/
|
|
75
|
+
private parseGitleaksResults;
|
|
76
|
+
/**
|
|
77
|
+
* Fallback mock scan when gitleaks not available
|
|
78
|
+
*/
|
|
79
|
+
private fallbackMockScan;
|
|
80
|
+
/**
|
|
81
|
+
* Calculate findings summary
|
|
82
|
+
*/
|
|
83
|
+
private calculateSummary;
|
|
84
|
+
/**
|
|
85
|
+
* Generate budget check based on security config
|
|
86
|
+
*/
|
|
87
|
+
private generateBudgetCheck;
|
|
88
|
+
/**
|
|
89
|
+
* Get empty summary structure
|
|
90
|
+
*/
|
|
91
|
+
private getEmptySummary;
|
|
92
|
+
/**
|
|
93
|
+
* Get default budget check structure
|
|
94
|
+
*/
|
|
95
|
+
private getDefaultBudgetCheck;
|
|
96
|
+
/**
|
|
97
|
+
* Generate JUnit XML report
|
|
98
|
+
*/
|
|
99
|
+
private generateJUnit;
|
|
100
|
+
/**
|
|
101
|
+
* Validate gitleaks scan configuration
|
|
102
|
+
*/
|
|
103
|
+
static validateConfig(config: GitleaksConfig): {
|
|
104
|
+
valid: boolean;
|
|
105
|
+
errors: string[];
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Check if Gitleaks is available
|
|
109
|
+
*/
|
|
110
|
+
static isAvailable(): Promise<{
|
|
111
|
+
available: boolean;
|
|
112
|
+
error?: string;
|
|
113
|
+
}>;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=gitleaks-secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitleaks-secrets.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/gitleaks-secrets.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,WAAW,EAAE;QACX,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,GAAE,MAAsB;IAK9C;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAkCzE;;OAEG;YACW,aAAa;IA+D3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAmD1B;;OAEG;YACW,eAAe;IA+E7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAgD5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAexB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA0BxB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAQ3B;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAM7B;;OAEG;IACH,OAAO,CAAC,aAAa;IAyBrB;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAqBnF;;OAEG;WACU,WAAW,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CA2B5E"}
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Gitleaks Secrets Adapter (Sécurité Réelle)
|
|
3
|
+
* Scan for hardcoded secrets in source code
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
import { existsSync, writeFileSync, unlinkSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { SecurityRedactor } from '../security/redactor.js';
|
|
9
|
+
export class GitleaksSecretsAdapter {
|
|
10
|
+
redactor;
|
|
11
|
+
workingDir;
|
|
12
|
+
constructor(workingDir = process.cwd()) {
|
|
13
|
+
this.redactor = SecurityRedactor.forLogs();
|
|
14
|
+
this.workingDir = workingDir;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Execute Gitleaks secrets scan
|
|
18
|
+
*/
|
|
19
|
+
async runSecretsScan(config) {
|
|
20
|
+
try {
|
|
21
|
+
console.log('🔍 Running Gitleaks secrets scan');
|
|
22
|
+
// Generate gitleaks config if needed
|
|
23
|
+
const configPath = await this.prepareConfig(config);
|
|
24
|
+
// Execute gitleaks
|
|
25
|
+
const result = await this.executeGitleaks(configPath, config);
|
|
26
|
+
// Cleanup temp config
|
|
27
|
+
if (configPath && configPath.includes('gitleaks-temp')) {
|
|
28
|
+
try {
|
|
29
|
+
unlinkSync(configPath);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Ignore cleanup errors
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
findings: [],
|
|
41
|
+
summary: this.getEmptySummary(),
|
|
42
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
43
|
+
scannedPath: config.workingDir,
|
|
44
|
+
error: this.redactor.redact(error instanceof Error ? error.message : 'Unknown error'),
|
|
45
|
+
errorCode: 'GL099'
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Prepare gitleaks configuration
|
|
51
|
+
*/
|
|
52
|
+
async prepareConfig(config) {
|
|
53
|
+
if (config.configFile && existsSync(config.configFile)) {
|
|
54
|
+
return config.configFile;
|
|
55
|
+
}
|
|
56
|
+
// Generate default config with QA360 exclusions
|
|
57
|
+
const gitleaksConfig = {
|
|
58
|
+
title: 'QA360 Gitleaks Config',
|
|
59
|
+
extend: {
|
|
60
|
+
useDefault: true
|
|
61
|
+
},
|
|
62
|
+
allowlist: {
|
|
63
|
+
description: 'QA360 allowlist',
|
|
64
|
+
paths: [
|
|
65
|
+
'.qa360/**',
|
|
66
|
+
'node_modules/**',
|
|
67
|
+
'dist/**',
|
|
68
|
+
'build/**',
|
|
69
|
+
'coverage/**',
|
|
70
|
+
'**/*.min.js',
|
|
71
|
+
'**/*.map',
|
|
72
|
+
...(config.excludePaths || [])
|
|
73
|
+
],
|
|
74
|
+
regexes: [
|
|
75
|
+
'example',
|
|
76
|
+
'test.*key',
|
|
77
|
+
'dummy.*secret',
|
|
78
|
+
'placeholder',
|
|
79
|
+
'YOUR_.*_HERE'
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
rules: [
|
|
83
|
+
{
|
|
84
|
+
id: 'generic-api-key',
|
|
85
|
+
description: 'Generic API Key',
|
|
86
|
+
regex: '(?i)\\b[a-z0-9]{32,}\\b',
|
|
87
|
+
entropy: 3.5,
|
|
88
|
+
keywords: ['api', 'key', 'token']
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: 'jwt-token',
|
|
92
|
+
description: 'JWT Token',
|
|
93
|
+
regex: 'eyJ[A-Za-z0-9_/+-]*\\.eyJ[A-Za-z0-9_/+-]*\\.[A-Za-z0-9_/+-]*',
|
|
94
|
+
keywords: ['jwt', 'token']
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'private-key',
|
|
98
|
+
description: 'Private Key',
|
|
99
|
+
regex: '-----BEGIN [A-Z]+ PRIVATE KEY-----',
|
|
100
|
+
keywords: ['private', 'key']
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
};
|
|
104
|
+
const configPath = join(config.workingDir, 'gitleaks-temp.toml');
|
|
105
|
+
// Convert to TOML format (simplified)
|
|
106
|
+
const tomlConfig = this.generateTomlConfig(gitleaksConfig);
|
|
107
|
+
writeFileSync(configPath, tomlConfig);
|
|
108
|
+
return configPath;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Generate TOML config from object
|
|
112
|
+
*/
|
|
113
|
+
generateTomlConfig(config) {
|
|
114
|
+
let toml = `title = "${config.title}"\n\n`;
|
|
115
|
+
if (config.extend?.useDefault) {
|
|
116
|
+
toml += '[extend]\nuseDefault = true\n\n';
|
|
117
|
+
}
|
|
118
|
+
if (config.allowlist) {
|
|
119
|
+
toml += '[allowlist]\n';
|
|
120
|
+
toml += `description = "${config.allowlist.description}"\n`;
|
|
121
|
+
if (config.allowlist.paths) {
|
|
122
|
+
toml += 'paths = [\n';
|
|
123
|
+
for (const path of config.allowlist.paths) {
|
|
124
|
+
toml += ` "${path}",\n`;
|
|
125
|
+
}
|
|
126
|
+
toml += ']\n';
|
|
127
|
+
}
|
|
128
|
+
if (config.allowlist.regexes) {
|
|
129
|
+
toml += 'regexes = [\n';
|
|
130
|
+
for (const regex of config.allowlist.regexes) {
|
|
131
|
+
toml += ` "${regex}",\n`;
|
|
132
|
+
}
|
|
133
|
+
toml += ']\n\n';
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (config.rules) {
|
|
137
|
+
for (const rule of config.rules) {
|
|
138
|
+
toml += `[[rules]]\n`;
|
|
139
|
+
toml += `id = "${rule.id}"\n`;
|
|
140
|
+
toml += `description = "${rule.description}"\n`;
|
|
141
|
+
toml += `regex = '''${rule.regex}'''\n`;
|
|
142
|
+
if (rule.entropy) {
|
|
143
|
+
toml += `entropy = ${rule.entropy}\n`;
|
|
144
|
+
}
|
|
145
|
+
if (rule.keywords) {
|
|
146
|
+
toml += 'keywords = [\n';
|
|
147
|
+
for (const keyword of rule.keywords) {
|
|
148
|
+
toml += ` "${keyword}",\n`;
|
|
149
|
+
}
|
|
150
|
+
toml += ']\n';
|
|
151
|
+
}
|
|
152
|
+
toml += '\n';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return toml;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Execute gitleaks scanner
|
|
159
|
+
*/
|
|
160
|
+
async executeGitleaks(configPath, config) {
|
|
161
|
+
return new Promise((resolve) => {
|
|
162
|
+
let output = '';
|
|
163
|
+
let error = '';
|
|
164
|
+
const args = [
|
|
165
|
+
'detect',
|
|
166
|
+
'--source', config.workingDir,
|
|
167
|
+
'--report-format', 'json',
|
|
168
|
+
'--no-git'
|
|
169
|
+
];
|
|
170
|
+
if (configPath) {
|
|
171
|
+
args.push('--config', configPath);
|
|
172
|
+
}
|
|
173
|
+
if (config.verbose) {
|
|
174
|
+
args.push('--verbose');
|
|
175
|
+
}
|
|
176
|
+
const child = spawn('gitleaks', args, {
|
|
177
|
+
cwd: config.workingDir,
|
|
178
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
179
|
+
});
|
|
180
|
+
const timeout = setTimeout(() => {
|
|
181
|
+
child.kill('SIGKILL');
|
|
182
|
+
resolve({
|
|
183
|
+
success: false,
|
|
184
|
+
findings: [],
|
|
185
|
+
summary: this.getEmptySummary(),
|
|
186
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
187
|
+
scannedPath: config.workingDir,
|
|
188
|
+
error: `Gitleaks scan timed out after ${config.timeout || 120000}ms`,
|
|
189
|
+
errorCode: 'GL001'
|
|
190
|
+
});
|
|
191
|
+
}, config.timeout || 120000);
|
|
192
|
+
if (child.stdout) {
|
|
193
|
+
child.stdout.on('data', (data) => {
|
|
194
|
+
output += data.toString();
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (child.stderr) {
|
|
198
|
+
child.stderr.on('data', (data) => {
|
|
199
|
+
error += data.toString();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
child.on('close', (code) => {
|
|
203
|
+
clearTimeout(timeout);
|
|
204
|
+
if (code === 0 || code === 1) { // 0 = no secrets, 1 = secrets found
|
|
205
|
+
const result = this.parseGitleaksResults(output, config);
|
|
206
|
+
resolve(result);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
resolve({
|
|
210
|
+
success: false,
|
|
211
|
+
findings: [],
|
|
212
|
+
summary: this.getEmptySummary(),
|
|
213
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
214
|
+
scannedPath: config.workingDir,
|
|
215
|
+
error: this.redactor.redact(error || `Gitleaks exited with code ${code}`),
|
|
216
|
+
rawOutput: this.redactor.redact(output),
|
|
217
|
+
errorCode: 'GL002'
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
child.on('error', (err) => {
|
|
222
|
+
clearTimeout(timeout);
|
|
223
|
+
// Fallback to mock scan
|
|
224
|
+
resolve(this.fallbackMockScan(config));
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Parse gitleaks results
|
|
230
|
+
*/
|
|
231
|
+
parseGitleaksResults(output, config) {
|
|
232
|
+
try {
|
|
233
|
+
let findings = [];
|
|
234
|
+
if (output.trim()) {
|
|
235
|
+
const data = JSON.parse(output);
|
|
236
|
+
findings = Array.isArray(data) ? data : [data];
|
|
237
|
+
}
|
|
238
|
+
// Redact sensitive information from findings
|
|
239
|
+
const redactedFindings = findings.map(finding => ({
|
|
240
|
+
...finding,
|
|
241
|
+
Secret: this.redactor.redact(finding.Secret || ''),
|
|
242
|
+
Match: this.redactor.redact(finding.Match || ''),
|
|
243
|
+
Email: this.redactor.redact(finding.Email || ''),
|
|
244
|
+
Author: this.redactor.redact(finding.Author || '')
|
|
245
|
+
}));
|
|
246
|
+
const summary = this.calculateSummary(redactedFindings);
|
|
247
|
+
const budgetCheck = this.generateBudgetCheck(summary, config);
|
|
248
|
+
const success = budgetCheck.findings_passed;
|
|
249
|
+
const junit = this.generateJUnit(summary, success, config);
|
|
250
|
+
return {
|
|
251
|
+
success,
|
|
252
|
+
findings: redactedFindings,
|
|
253
|
+
summary,
|
|
254
|
+
budgetCheck,
|
|
255
|
+
scannedPath: config.workingDir,
|
|
256
|
+
rawOutput: this.redactor.redact(output),
|
|
257
|
+
junit
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
return {
|
|
262
|
+
success: false,
|
|
263
|
+
findings: [],
|
|
264
|
+
summary: this.getEmptySummary(),
|
|
265
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
266
|
+
scannedPath: config.workingDir,
|
|
267
|
+
error: `Failed to parse Gitleaks results: ${error}`,
|
|
268
|
+
rawOutput: this.redactor.redact(output),
|
|
269
|
+
errorCode: 'GL003'
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Fallback mock scan when gitleaks not available
|
|
275
|
+
*/
|
|
276
|
+
fallbackMockScan(config) {
|
|
277
|
+
console.log('⚠️ Gitleaks not found, using mock scan (install gitleaks for real scanning)');
|
|
278
|
+
return {
|
|
279
|
+
success: true,
|
|
280
|
+
findings: [],
|
|
281
|
+
summary: this.getEmptySummary(),
|
|
282
|
+
budgetCheck: this.getDefaultBudgetCheck(),
|
|
283
|
+
scannedPath: config.workingDir,
|
|
284
|
+
error: 'Gitleaks not available - install from https://github.com/gitleaks/gitleaks',
|
|
285
|
+
junit: this.generateJUnit(this.getEmptySummary(), true, config),
|
|
286
|
+
errorCode: 'GL004'
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Calculate findings summary
|
|
291
|
+
*/
|
|
292
|
+
calculateSummary(findings) {
|
|
293
|
+
const summary = {
|
|
294
|
+
total: findings.length,
|
|
295
|
+
by_rule: {},
|
|
296
|
+
by_file: {},
|
|
297
|
+
high_entropy: 0
|
|
298
|
+
};
|
|
299
|
+
for (const finding of findings) {
|
|
300
|
+
// Count by rule
|
|
301
|
+
const ruleId = finding.RuleID || 'unknown';
|
|
302
|
+
summary.by_rule[ruleId] = (summary.by_rule[ruleId] || 0) + 1;
|
|
303
|
+
// Count by file
|
|
304
|
+
const file = finding.File || 'unknown';
|
|
305
|
+
summary.by_file[file] = (summary.by_file[file] || 0) + 1;
|
|
306
|
+
// Count high entropy findings
|
|
307
|
+
if (finding.Entropy && finding.Entropy > 4.0) {
|
|
308
|
+
summary.high_entropy++;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return summary;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Generate budget check based on security config
|
|
315
|
+
*/
|
|
316
|
+
generateBudgetCheck(summary, config) {
|
|
317
|
+
const secrets = config.security?.secrets;
|
|
318
|
+
return {
|
|
319
|
+
findings_passed: !secrets?.max_findings || summary.total <= secrets.max_findings
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Get empty summary structure
|
|
324
|
+
*/
|
|
325
|
+
getEmptySummary() {
|
|
326
|
+
return {
|
|
327
|
+
total: 0,
|
|
328
|
+
by_rule: {},
|
|
329
|
+
by_file: {},
|
|
330
|
+
high_entropy: 0
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Get default budget check structure
|
|
335
|
+
*/
|
|
336
|
+
getDefaultBudgetCheck() {
|
|
337
|
+
return {
|
|
338
|
+
findings_passed: true
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Generate JUnit XML report
|
|
343
|
+
*/
|
|
344
|
+
generateJUnit(summary, success, config) {
|
|
345
|
+
const timestamp = new Date().toISOString();
|
|
346
|
+
const duration = '0.1';
|
|
347
|
+
let junit = `<?xml version="1.0" encoding="UTF-8"?>
|
|
348
|
+
<testsuite name="Gitleaks Secrets Scan" tests="1" failures="${success ? 0 : 1}" time="${duration}" timestamp="${timestamp}">
|
|
349
|
+
<testcase name="Secret Detection: ${config.workingDir}" time="${duration}">
|
|
350
|
+
`;
|
|
351
|
+
if (!success) {
|
|
352
|
+
junit += ` <failure message="Secrets found in source code">
|
|
353
|
+
Total findings: ${summary.total}
|
|
354
|
+
High entropy: ${summary.high_entropy}
|
|
355
|
+
Files affected: ${Object.keys(summary.by_file).length}
|
|
356
|
+
Rules triggered: ${Object.keys(summary.by_rule).length}
|
|
357
|
+
</failure>
|
|
358
|
+
`;
|
|
359
|
+
}
|
|
360
|
+
junit += ` </testcase>
|
|
361
|
+
</testsuite>`;
|
|
362
|
+
return junit;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Validate gitleaks scan configuration
|
|
366
|
+
*/
|
|
367
|
+
static validateConfig(config) {
|
|
368
|
+
const errors = [];
|
|
369
|
+
if (!config.workingDir) {
|
|
370
|
+
errors.push('Working directory is required');
|
|
371
|
+
}
|
|
372
|
+
if (!existsSync(config.workingDir)) {
|
|
373
|
+
errors.push(`Working directory does not exist: ${config.workingDir}`);
|
|
374
|
+
}
|
|
375
|
+
if (config.configFile && !existsSync(config.configFile)) {
|
|
376
|
+
errors.push(`Config file does not exist: ${config.configFile}`);
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
valid: errors.length === 0,
|
|
380
|
+
errors
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Check if Gitleaks is available
|
|
385
|
+
*/
|
|
386
|
+
static async isAvailable() {
|
|
387
|
+
return new Promise((resolve) => {
|
|
388
|
+
const child = spawn('gitleaks', ['version'], { stdio: 'pipe' });
|
|
389
|
+
child.on('close', (code) => {
|
|
390
|
+
resolve({
|
|
391
|
+
available: code === 0,
|
|
392
|
+
error: code !== 0 ? 'gitleaks not found. Install from https://github.com/gitleaks/gitleaks' : undefined
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
child.on('error', () => {
|
|
396
|
+
resolve({
|
|
397
|
+
available: false,
|
|
398
|
+
error: 'gitleaks not found. Install from https://github.com/gitleaks/gitleaks'
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
setTimeout(() => {
|
|
402
|
+
child.kill();
|
|
403
|
+
resolve({
|
|
404
|
+
available: false,
|
|
405
|
+
error: 'gitleaks check timed out'
|
|
406
|
+
});
|
|
407
|
+
}, 5000);
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 k6 Performance Adapter (Socle OOTB)
|
|
3
|
+
* Short performance tests with P95 calculation
|
|
4
|
+
*/
|
|
5
|
+
import { PackBudgets } from '../types/pack-v1.js';
|
|
6
|
+
export interface K6TestConfig {
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
budgets?: PackBudgets;
|
|
9
|
+
duration?: string;
|
|
10
|
+
vus?: number;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
scenarios?: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
export interface K6TestResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
metrics: {
|
|
17
|
+
http_req_duration_p95: number;
|
|
18
|
+
http_req_duration_avg: number;
|
|
19
|
+
http_req_failed_rate: number;
|
|
20
|
+
http_reqs_total: number;
|
|
21
|
+
vus_max: number;
|
|
22
|
+
iterations: number;
|
|
23
|
+
};
|
|
24
|
+
thresholds: {
|
|
25
|
+
[key: string]: {
|
|
26
|
+
passed: boolean;
|
|
27
|
+
value: number;
|
|
28
|
+
threshold: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
error?: string;
|
|
32
|
+
rawOutput?: string;
|
|
33
|
+
junit?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare class K6PerfAdapter {
|
|
36
|
+
private redactor;
|
|
37
|
+
private workingDir;
|
|
38
|
+
constructor(workingDir?: string);
|
|
39
|
+
/**
|
|
40
|
+
* Execute k6 performance test
|
|
41
|
+
*/
|
|
42
|
+
runPerfTest(config: K6TestConfig): Promise<K6TestResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Generate k6 test script
|
|
45
|
+
*/
|
|
46
|
+
private generateK6Script;
|
|
47
|
+
/**
|
|
48
|
+
* Execute k6 command
|
|
49
|
+
*/
|
|
50
|
+
private executeK6;
|
|
51
|
+
/**
|
|
52
|
+
* Parse k6 results from output
|
|
53
|
+
*/
|
|
54
|
+
private parseK6Results;
|
|
55
|
+
/**
|
|
56
|
+
* Parse metrics from text output
|
|
57
|
+
*/
|
|
58
|
+
private parseTextMetrics;
|
|
59
|
+
/**
|
|
60
|
+
* Parse thresholds from text output
|
|
61
|
+
*/
|
|
62
|
+
private parseTextThresholds;
|
|
63
|
+
/**
|
|
64
|
+
* Generate JUnit XML
|
|
65
|
+
*/
|
|
66
|
+
private generateJUnit;
|
|
67
|
+
/**
|
|
68
|
+
* Get default metrics structure
|
|
69
|
+
*/
|
|
70
|
+
private getDefaultMetrics;
|
|
71
|
+
/**
|
|
72
|
+
* Check if k6 is available
|
|
73
|
+
*/
|
|
74
|
+
static isAvailable(): Promise<{
|
|
75
|
+
available: boolean;
|
|
76
|
+
error?: string;
|
|
77
|
+
}>;
|
|
78
|
+
/**
|
|
79
|
+
* Validate performance test configuration
|
|
80
|
+
*/
|
|
81
|
+
static validateConfig(config: K6TestConfig): {
|
|
82
|
+
valid: boolean;
|
|
83
|
+
errors: string[];
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=k6-perf.d.ts.map
|