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,225 @@
1
+ /**
2
+ * QA360 Secrets Cryptography
3
+ * AES-256-GCM encryption with PBKDF2 key derivation
4
+ */
5
+ import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from 'crypto';
6
+ export class SecretsCrypto {
7
+ static ALGORITHM = 'aes-256-gcm';
8
+ static KEY_LENGTH = 32; // 256 bits
9
+ static IV_LENGTH = 16; // 128 bits
10
+ static SALT_LENGTH = 32; // 256 bits
11
+ static TAG_LENGTH = 16; // 128 bits
12
+ static ITERATIONS = 100000; // PBKDF2 iterations
13
+ /**
14
+ * Encrypt secrets with password-derived key
15
+ */
16
+ static encrypt(secrets, password) {
17
+ try {
18
+ // Generate random salt and IV
19
+ const salt = randomBytes(this.SALT_LENGTH);
20
+ const iv = randomBytes(this.IV_LENGTH);
21
+ // Derive key from password using PBKDF2
22
+ const key = pbkdf2Sync(password, salt, this.ITERATIONS, this.KEY_LENGTH, 'sha256');
23
+ // Serialize secrets
24
+ const plaintext = JSON.stringify(secrets);
25
+ // Encrypt using AES-256-GCM
26
+ const cipher = createCipheriv(this.ALGORITHM, key, iv);
27
+ cipher.setAAD(Buffer.from('qa360-secrets-v1'));
28
+ let encrypted = cipher.update(plaintext, 'utf8', 'hex');
29
+ encrypted += cipher.final('hex');
30
+ const tag = cipher.getAuthTag();
31
+ // Create encrypted data structure
32
+ const encryptedData = {
33
+ data: encrypted,
34
+ iv: iv.toString('hex'),
35
+ salt: salt.toString('hex'),
36
+ tag: tag.toString('hex'),
37
+ algorithm: this.ALGORITHM,
38
+ iterations: this.ITERATIONS
39
+ };
40
+ // Calculate checksum for integrity
41
+ const checksum = this.calculateChecksum(encryptedData);
42
+ return {
43
+ version: '1.0.0',
44
+ encrypted: encryptedData,
45
+ checksum
46
+ };
47
+ }
48
+ catch (error) {
49
+ throw new Error(`Encryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
50
+ }
51
+ }
52
+ /**
53
+ * Decrypt secrets with password
54
+ */
55
+ static decrypt(store, password) {
56
+ try {
57
+ // Verify checksum
58
+ const expectedChecksum = this.calculateChecksum(store.encrypted);
59
+ if (store.checksum !== expectedChecksum) {
60
+ throw new Error('Secrets store integrity check failed');
61
+ }
62
+ const { data, iv, salt, tag, algorithm, iterations } = store.encrypted;
63
+ // Verify algorithm
64
+ if (algorithm !== this.ALGORITHM) {
65
+ throw new Error(`Unsupported encryption algorithm: ${algorithm}`);
66
+ }
67
+ // Derive key from password
68
+ const key = pbkdf2Sync(password, Buffer.from(salt, 'hex'), iterations, this.KEY_LENGTH, 'sha256');
69
+ // Decrypt using AES-256-GCM
70
+ const decipher = createDecipheriv(algorithm, key, Buffer.from(iv, 'hex'));
71
+ decipher.setAAD(Buffer.from('qa360-secrets-v1'));
72
+ decipher.setAuthTag(Buffer.from(tag, 'hex'));
73
+ let decrypted = decipher.update(data, 'hex', 'utf8');
74
+ decrypted += decipher.final('utf8');
75
+ // Parse secrets
76
+ return JSON.parse(decrypted);
77
+ }
78
+ catch (error) {
79
+ if (error instanceof Error && /unsupported state|authenticity|bad decrypt|unable to decrypt/i.test(error.message)) {
80
+ throw new Error('Invalid password or corrupted secrets store');
81
+ }
82
+ throw new Error(`Decryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
83
+ }
84
+ }
85
+ /**
86
+ * Generate a secure random password
87
+ */
88
+ static generatePassword(length = 32) {
89
+ const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*';
90
+ let password = '';
91
+ for (let i = 0; i < length; i++) {
92
+ const randomIndex = Math.floor(Math.random() * charset.length);
93
+ password += charset[randomIndex];
94
+ }
95
+ return password;
96
+ }
97
+ /**
98
+ * Derive password from system keychain or environment
99
+ */
100
+ static async deriveSystemPassword() {
101
+ // Try to get from environment first (for CI/CD)
102
+ const envPassword = process.env.QA360_SECRETS_PASSWORD;
103
+ if (envPassword) {
104
+ return envPassword;
105
+ }
106
+ // Try to get from system keychain (macOS/Linux)
107
+ try {
108
+ if (process.platform === 'darwin') {
109
+ return await this.getMacOSKeychainPassword();
110
+ }
111
+ else if (process.platform === 'linux') {
112
+ return await this.getLinuxKeychainPassword();
113
+ }
114
+ else if (process.platform === 'win32') {
115
+ return await this.getWindowsCredentialPassword();
116
+ }
117
+ }
118
+ catch (error) {
119
+ // Fallback to machine-specific derived password
120
+ console.warn('System keychain unavailable, using machine-derived password');
121
+ }
122
+ // Generate machine-specific password as fallback
123
+ return this.generateMachinePassword();
124
+ }
125
+ /**
126
+ * Get password from macOS Keychain
127
+ */
128
+ static async getMacOSKeychainPassword() {
129
+ const { execSync } = require('child_process');
130
+ try {
131
+ // Try to get existing password
132
+ const result = execSync('security find-generic-password -a qa360 -s "QA360 Secrets" -w', { encoding: 'utf8', stdio: 'pipe' });
133
+ return result.trim();
134
+ }
135
+ catch (error) {
136
+ // Create new password if not found
137
+ const password = this.generatePassword();
138
+ execSync(`security add-generic-password -a qa360 -s "QA360 Secrets" -w "${password}"`, { stdio: 'pipe' });
139
+ return password;
140
+ }
141
+ }
142
+ /**
143
+ * Get password from Linux keyring (using secret-tool)
144
+ */
145
+ static async getLinuxKeychainPassword() {
146
+ const { execSync } = require('child_process');
147
+ try {
148
+ // Try to get existing password
149
+ const result = execSync('secret-tool lookup application qa360 service secrets', { encoding: 'utf8', stdio: 'pipe' });
150
+ return result.trim();
151
+ }
152
+ catch (error) {
153
+ // Create new password if not found
154
+ const password = this.generatePassword();
155
+ execSync(`echo "${password}" | secret-tool store --label="QA360 Secrets" application qa360 service secrets`, { stdio: 'pipe' });
156
+ return password;
157
+ }
158
+ }
159
+ /**
160
+ * Get password from Windows Credential Manager
161
+ */
162
+ static async getWindowsCredentialPassword() {
163
+ const { execSync } = require('child_process');
164
+ try {
165
+ // Try to get existing password
166
+ const result = execSync('powershell -Command "Get-StoredCredential -Target QA360-Secrets | Select-Object -ExpandProperty Password"', { encoding: 'utf8', stdio: 'pipe' });
167
+ return result.trim();
168
+ }
169
+ catch (error) {
170
+ // Create new password if not found
171
+ const password = this.generatePassword();
172
+ execSync(`powershell -Command "New-StoredCredential -Target QA360-Secrets -UserName qa360 -Password '${password}'"`, { stdio: 'pipe' });
173
+ return password;
174
+ }
175
+ }
176
+ /**
177
+ * Generate machine-specific password as fallback
178
+ */
179
+ static generateMachinePassword() {
180
+ const os = require('os');
181
+ // Collect machine-specific data
182
+ const machineData = [
183
+ os.hostname(),
184
+ os.platform(),
185
+ os.arch(),
186
+ os.userInfo().username,
187
+ process.env.HOME || process.env.USERPROFILE || '',
188
+ ].join('|');
189
+ // Hash machine data to create consistent password
190
+ const hash = createHash('sha256').update(machineData + 'qa360-secrets-salt').digest('hex');
191
+ // Convert to password format
192
+ return hash.substring(0, 32);
193
+ }
194
+ /**
195
+ * Calculate checksum for integrity verification
196
+ */
197
+ static calculateChecksum(data) {
198
+ const content = `${data.data}${data.iv}${data.salt}${data.tag}${data.algorithm}${data.iterations}`;
199
+ return createHash('sha256').update(content).digest('hex');
200
+ }
201
+ /**
202
+ * Redact secret value for logging
203
+ */
204
+ static redactSecret(value) {
205
+ const n = value.length;
206
+ if (n <= 4)
207
+ return '***';
208
+ if (n <= 8)
209
+ return value.slice(0, 2) + '***' + value.slice(-2);
210
+ return value.slice(0, 3) + '***' + value.slice(-3);
211
+ }
212
+ /**
213
+ * Check if a string looks like a secret
214
+ */
215
+ static looksLikeSecret(value) {
216
+ if (value.length < 8)
217
+ return false;
218
+ const RX_BASE64 = /^(?:[A-Za-z0-9+/]{16,}={0,2})$/;
219
+ const RX_OPENAI = /^sk-[A-Za-z0-9]{20,}$/;
220
+ const RX_GH = /^ghp_[A-Za-z0-9]{36,}$/;
221
+ const RX_SLACK = /^xox[baprs]-\d{10,}-\d{10,}-[A-Za-z0-9]{10,}$/;
222
+ const RX_HEX = /^(?:[A-Fa-f0-9]{32,})$/;
223
+ return RX_OPENAI.test(value) || RX_GH.test(value) || RX_SLACK.test(value) || RX_BASE64.test(value) || RX_HEX.test(value);
224
+ }
225
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * QA360 Secrets Manager
3
+ * Manages encrypted secrets storage and retrieval
4
+ */
5
+ export interface SecretsManagerOptions {
6
+ qa360Dir?: string;
7
+ password?: string;
8
+ }
9
+ export declare class SecretsManager {
10
+ private qa360Dir;
11
+ private secretsPath;
12
+ private password;
13
+ private cache;
14
+ constructor(options?: SecretsManagerOptions);
15
+ /**
16
+ * Initialize secrets manager with password
17
+ */
18
+ initialize(password?: string): Promise<void>;
19
+ /**
20
+ * Add or update a secret
21
+ */
22
+ addSecret(name: string, value: string): Promise<void>;
23
+ /**
24
+ * Get a secret value
25
+ */
26
+ getSecret(name: string): Promise<string | null>;
27
+ /**
28
+ * List all secret names (without values)
29
+ */
30
+ listSecrets(): Promise<Array<{
31
+ name: string;
32
+ createdAt: string;
33
+ updatedAt: string;
34
+ }>>;
35
+ /**
36
+ * Remove a secret
37
+ */
38
+ removeSecret(name: string): Promise<boolean>;
39
+ /**
40
+ * Check if secrets store exists and is accessible
41
+ */
42
+ doctor(): Promise<{
43
+ exists: boolean;
44
+ accessible: boolean;
45
+ count: number;
46
+ errors: string[];
47
+ }>;
48
+ /**
49
+ * Export secrets in redacted format for debugging
50
+ */
51
+ exportRedacted(): Promise<Record<string, string>>;
52
+ /**
53
+ * Interpolate secrets in a string
54
+ */
55
+ interpolateSecrets(text: string): Promise<string>;
56
+ /**
57
+ * Find secret references in text
58
+ */
59
+ findSecretReferences(text: string): string[];
60
+ /**
61
+ * Validate that all referenced secrets exist
62
+ */
63
+ validateSecretReferences(text: string): Promise<string[]>;
64
+ /**
65
+ * Load secrets from encrypted store
66
+ */
67
+ private loadSecrets;
68
+ /**
69
+ * Save secrets to encrypted store
70
+ */
71
+ private saveSecrets;
72
+ /**
73
+ * Ensure manager is initialized
74
+ */
75
+ private ensureInitialized;
76
+ }
77
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/core/secrets/manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,KAAK,CAA4C;gBAE7C,OAAO,GAAE,qBAA0B;IAU/C;;OAEG;IACG,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlD;;OAEG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B3D;;OAEG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAOrD;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAW3F;;OAEG;IACG,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBlD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC;QACtB,MAAM,EAAE,OAAO,CAAC;QAChB,UAAU,EAAE,OAAO,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IAsBF;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAavD;;OAEG;IACG,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA8BvD;;OAEG;IACH,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAU5C;;OAEG;IACG,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAU/D;;OAEG;YACW,WAAW;IAqBzB;;OAEG;YACW,WAAW;IAgBzB;;OAEG;YACW,iBAAiB;CAKhC"}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * QA360 Secrets Manager
3
+ * Manages encrypted secrets storage and retrieval
4
+ */
5
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { SecretsCrypto } from './crypto.js';
8
+ export class SecretsManager {
9
+ qa360Dir;
10
+ secretsPath;
11
+ password = null;
12
+ cache = null;
13
+ constructor(options = {}) {
14
+ this.qa360Dir = options.qa360Dir || join(process.cwd(), '.qa360');
15
+ this.secretsPath = join(this.qa360Dir, 'secrets.enc');
16
+ // Ensure .qa360 directory exists
17
+ if (!existsSync(this.qa360Dir)) {
18
+ mkdirSync(this.qa360Dir, { recursive: true });
19
+ }
20
+ }
21
+ /**
22
+ * Initialize secrets manager with password
23
+ */
24
+ async initialize(password) {
25
+ if (password) {
26
+ this.password = password;
27
+ }
28
+ else {
29
+ this.password = await SecretsCrypto.deriveSystemPassword();
30
+ }
31
+ }
32
+ /**
33
+ * Add or update a secret
34
+ */
35
+ async addSecret(name, value) {
36
+ await this.ensureInitialized();
37
+ // Validate secret name
38
+ if (!/^[A-Z_][A-Z0-9_]*$/.test(name)) {
39
+ throw new Error('Secret name must be uppercase with underscores only (e.g., API_TOKEN)');
40
+ }
41
+ // Load existing secrets
42
+ const secrets = await this.loadSecrets();
43
+ // Add or update secret
44
+ secrets[name] = {
45
+ name,
46
+ value,
47
+ createdAt: secrets[name]?.createdAt || new Date().toISOString(),
48
+ updatedAt: new Date().toISOString()
49
+ };
50
+ // Save encrypted secrets
51
+ await this.saveSecrets(secrets);
52
+ // Clear cache
53
+ this.cache = null;
54
+ }
55
+ /**
56
+ * Get a secret value
57
+ */
58
+ async getSecret(name) {
59
+ await this.ensureInitialized();
60
+ const secrets = await this.loadSecrets();
61
+ return secrets[name]?.value || null;
62
+ }
63
+ /**
64
+ * List all secret names (without values)
65
+ */
66
+ async listSecrets() {
67
+ await this.ensureInitialized();
68
+ const secrets = await this.loadSecrets();
69
+ return Object.values(secrets).map(({ name, createdAt, updatedAt }) => ({
70
+ name,
71
+ createdAt,
72
+ updatedAt
73
+ }));
74
+ }
75
+ /**
76
+ * Remove a secret
77
+ */
78
+ async removeSecret(name) {
79
+ await this.ensureInitialized();
80
+ const secrets = await this.loadSecrets();
81
+ if (!secrets[name]) {
82
+ return false;
83
+ }
84
+ delete secrets[name];
85
+ await this.saveSecrets(secrets);
86
+ // Clear cache
87
+ this.cache = null;
88
+ return true;
89
+ }
90
+ /**
91
+ * Check if secrets store exists and is accessible
92
+ */
93
+ async doctor() {
94
+ const errors = [];
95
+ let exists = false;
96
+ let accessible = false;
97
+ let count = 0;
98
+ try {
99
+ exists = existsSync(this.secretsPath);
100
+ if (exists) {
101
+ await this.ensureInitialized();
102
+ const secrets = await this.loadSecrets();
103
+ accessible = true;
104
+ count = Object.keys(secrets).length;
105
+ }
106
+ }
107
+ catch (error) {
108
+ errors.push(error instanceof Error ? error.message : 'Unknown error');
109
+ }
110
+ return { exists, accessible, count, errors };
111
+ }
112
+ /**
113
+ * Export secrets in redacted format for debugging
114
+ */
115
+ async exportRedacted() {
116
+ await this.ensureInitialized();
117
+ const secrets = await this.loadSecrets();
118
+ const redacted = {};
119
+ for (const [name, entry] of Object.entries(secrets)) {
120
+ redacted[name] = SecretsCrypto.redactSecret(entry.value);
121
+ }
122
+ return redacted;
123
+ }
124
+ /**
125
+ * Interpolate secrets in a string
126
+ */
127
+ async interpolateSecrets(text) {
128
+ await this.ensureInitialized();
129
+ // Find all secret references: ${{ secrets.SECRET_NAME }}
130
+ const secretRefs = text.match(/\$\{\{\s*secrets\.([A-Z_][A-Z0-9_]*)\s*\}\}/g);
131
+ if (!secretRefs) {
132
+ return text;
133
+ }
134
+ const secrets = await this.loadSecrets();
135
+ let result = text;
136
+ for (const ref of secretRefs) {
137
+ const match = ref.match(/\$\{\{\s*secrets\.([A-Z_][A-Z0-9_]*)\s*\}\}/);
138
+ if (match) {
139
+ const secretName = match[1];
140
+ const secretValue = secrets[secretName]?.value;
141
+ if (secretValue) {
142
+ result = result.replace(ref, secretValue);
143
+ }
144
+ else {
145
+ throw new Error(`Secret not found: ${secretName}`);
146
+ }
147
+ }
148
+ }
149
+ return result;
150
+ }
151
+ /**
152
+ * Find secret references in text
153
+ */
154
+ findSecretReferences(text) {
155
+ const matches = text.match(/\$\{\{\s*secrets\.([A-Z_][A-Z0-9_]*)\s*\}\}/g);
156
+ if (!matches)
157
+ return [];
158
+ return matches.map(match => {
159
+ const secretMatch = match.match(/\$\{\{\s*secrets\.([A-Z_][A-Z0-9_]*)\s*\}\}/);
160
+ return secretMatch ? secretMatch[1] : '';
161
+ }).filter(Boolean);
162
+ }
163
+ /**
164
+ * Validate that all referenced secrets exist
165
+ */
166
+ async validateSecretReferences(text) {
167
+ const references = this.findSecretReferences(text);
168
+ if (references.length === 0)
169
+ return [];
170
+ await this.ensureInitialized();
171
+ const secrets = await this.loadSecrets();
172
+ return references.filter(ref => !secrets[ref]);
173
+ }
174
+ /**
175
+ * Load secrets from encrypted store
176
+ */
177
+ async loadSecrets() {
178
+ if (this.cache) {
179
+ return this.cache;
180
+ }
181
+ if (!existsSync(this.secretsPath)) {
182
+ this.cache = {};
183
+ return this.cache;
184
+ }
185
+ try {
186
+ const encryptedContent = readFileSync(this.secretsPath, 'utf8');
187
+ const store = JSON.parse(encryptedContent);
188
+ this.cache = SecretsCrypto.decrypt(store, this.password);
189
+ return this.cache;
190
+ }
191
+ catch (error) {
192
+ throw new Error(`Failed to load secrets: ${error instanceof Error ? error.message : 'Unknown error'}`);
193
+ }
194
+ }
195
+ /**
196
+ * Save secrets to encrypted store
197
+ */
198
+ async saveSecrets(secrets) {
199
+ try {
200
+ const store = SecretsCrypto.encrypt(secrets, this.password);
201
+ const encryptedContent = JSON.stringify(store, null, 2);
202
+ writeFileSync(this.secretsPath, encryptedContent, 'utf8');
203
+ // Set restrictive permissions (owner read/write only)
204
+ chmodSync(this.secretsPath, 0o600);
205
+ this.cache = secrets;
206
+ }
207
+ catch (error) {
208
+ throw new Error(`Failed to save secrets: ${error instanceof Error ? error.message : 'Unknown error'}`);
209
+ }
210
+ }
211
+ /**
212
+ * Ensure manager is initialized
213
+ */
214
+ async ensureInitialized() {
215
+ if (!this.password) {
216
+ await this.initialize();
217
+ }
218
+ }
219
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * QA360 Extended Redaction Patterns
3
+ * Phase 7-Final: Security & Redaction Audit
4
+ */
5
+ export interface RedactionPattern {
6
+ name: string;
7
+ regex: RegExp;
8
+ replacement: string;
9
+ severity: 'critical' | 'high' | 'medium';
10
+ category: 'auth' | 'database' | 'api' | 'crypto' | 'pii';
11
+ }
12
+ /**
13
+ * Extended redaction patterns for Phase 7-Final
14
+ */
15
+ export declare const EXTENDED_REDACTION_PATTERNS: RedactionPattern[];
16
+ /**
17
+ * Apply all extended redaction patterns to text
18
+ */
19
+ export declare function applyExtendedRedaction(text: string): {
20
+ redacted: string;
21
+ patternsMatched: string[];
22
+ severity: 'critical' | 'high' | 'medium' | 'none';
23
+ };
24
+ /**
25
+ * Generate redaction audit report
26
+ */
27
+ export declare function generateRedactionAudit(original: string, redacted: string, patternsMatched: string[]): string;
28
+ //# sourceMappingURL=redaction-patterns-extended.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction-patterns-extended.d.ts","sourceRoot":"","sources":["../../../src/core/security/redaction-patterns-extended.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;IACzC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,KAAK,CAAC;CAC1D;AAED;;GAEG;AACH,eAAO,MAAM,2BAA2B,EAAE,gBAAgB,EAoLzD,CAAC;AAEF;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;CACnD,CAuBA;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EAAE,GACxB,MAAM,CAwCR"}