@qa-ai-stlc/runner-security 0.0.0-stage → 1.7.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 (94) hide show
  1. package/README.md +52 -2
  2. package/dist/checks/authz-check.d.ts +9 -0
  3. package/dist/checks/authz-check.d.ts.map +1 -0
  4. package/dist/checks/authz-check.js +100 -0
  5. package/dist/checks/authz-check.js.map +1 -0
  6. package/dist/checks/code-check-support.d.ts +9 -0
  7. package/dist/checks/code-check-support.d.ts.map +1 -0
  8. package/dist/checks/code-check-support.js +19 -0
  9. package/dist/checks/code-check-support.js.map +1 -0
  10. package/dist/checks/configuration-check.d.ts +10 -0
  11. package/dist/checks/configuration-check.d.ts.map +1 -0
  12. package/dist/checks/configuration-check.js +128 -0
  13. package/dist/checks/configuration-check.js.map +1 -0
  14. package/dist/checks/cookies-check.d.ts +10 -0
  15. package/dist/checks/cookies-check.d.ts.map +1 -0
  16. package/dist/checks/cookies-check.js +113 -0
  17. package/dist/checks/cookies-check.js.map +1 -0
  18. package/dist/checks/cors-check.d.ts +10 -0
  19. package/dist/checks/cors-check.d.ts.map +1 -0
  20. package/dist/checks/cors-check.js +55 -0
  21. package/dist/checks/cors-check.js.map +1 -0
  22. package/dist/checks/csrf-check.d.ts +10 -0
  23. package/dist/checks/csrf-check.d.ts.map +1 -0
  24. package/dist/checks/csrf-check.js +108 -0
  25. package/dist/checks/csrf-check.js.map +1 -0
  26. package/dist/checks/dependencies-check.d.ts +10 -0
  27. package/dist/checks/dependencies-check.d.ts.map +1 -0
  28. package/dist/checks/dependencies-check.js +160 -0
  29. package/dist/checks/dependencies-check.js.map +1 -0
  30. package/dist/checks/encoding-check.d.ts +10 -0
  31. package/dist/checks/encoding-check.d.ts.map +1 -0
  32. package/dist/checks/encoding-check.js +67 -0
  33. package/dist/checks/encoding-check.js.map +1 -0
  34. package/dist/checks/errors-check.d.ts +8 -0
  35. package/dist/checks/errors-check.d.ts.map +1 -0
  36. package/dist/checks/errors-check.js +60 -0
  37. package/dist/checks/errors-check.js.map +1 -0
  38. package/dist/checks/headers-check.d.ts +9 -0
  39. package/dist/checks/headers-check.d.ts.map +1 -0
  40. package/dist/checks/headers-check.js +110 -0
  41. package/dist/checks/headers-check.js.map +1 -0
  42. package/dist/checks/secrets-check.d.ts +10 -0
  43. package/dist/checks/secrets-check.d.ts.map +1 -0
  44. package/dist/checks/secrets-check.js +113 -0
  45. package/dist/checks/secrets-check.js.map +1 -0
  46. package/dist/checks/session-check.d.ts +10 -0
  47. package/dist/checks/session-check.d.ts.map +1 -0
  48. package/dist/checks/session-check.js +87 -0
  49. package/dist/checks/session-check.js.map +1 -0
  50. package/dist/checks/session-pages.d.ts +11 -0
  51. package/dist/checks/session-pages.d.ts.map +1 -0
  52. package/dist/checks/session-pages.js +25 -0
  53. package/dist/checks/session-pages.js.map +1 -0
  54. package/dist/cookie-flags.d.ts +14 -0
  55. package/dist/cookie-flags.d.ts.map +1 -0
  56. package/dist/cookie-flags.js +28 -0
  57. package/dist/cookie-flags.js.map +1 -0
  58. package/dist/default-checks.d.ts +4 -0
  59. package/dist/default-checks.d.ts.map +1 -0
  60. package/dist/default-checks.js +28 -0
  61. package/dist/default-checks.js.map +1 -0
  62. package/dist/fetch-page.d.ts +19 -0
  63. package/dist/fetch-page.d.ts.map +1 -0
  64. package/dist/fetch-page.js +36 -0
  65. package/dist/fetch-page.js.map +1 -0
  66. package/dist/finding.d.ts +8 -0
  67. package/dist/finding.d.ts.map +1 -0
  68. package/dist/finding.js +14 -0
  69. package/dist/finding.js.map +1 -0
  70. package/dist/identity-sessions.d.ts +42 -0
  71. package/dist/identity-sessions.d.ts.map +1 -0
  72. package/dist/identity-sessions.js +102 -0
  73. package/dist/identity-sessions.js.map +1 -0
  74. package/dist/index.d.ts +8 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +10 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/security-audit.d.ts +30 -0
  79. package/dist/security-audit.d.ts.map +1 -0
  80. package/dist/security-audit.js +173 -0
  81. package/dist/security-audit.js.map +1 -0
  82. package/dist/security-check.d.ts +30 -0
  83. package/dist/security-check.d.ts.map +1 -0
  84. package/dist/security-check.js +4 -0
  85. package/dist/security-check.js.map +1 -0
  86. package/dist/security-probe.d.ts +56 -0
  87. package/dist/security-probe.d.ts.map +1 -0
  88. package/dist/security-probe.js +128 -0
  89. package/dist/security-probe.js.map +1 -0
  90. package/dist/source-checkout.d.ts +56 -0
  91. package/dist/source-checkout.d.ts.map +1 -0
  92. package/dist/source-checkout.js +140 -0
  93. package/dist/source-checkout.js.map +1 -0
  94. package/package.json +36 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-sessions.js","sourceRoot":"","sources":["../src/identity-sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export * from './cookie-flags.js';
2
+ export * from './default-checks.js';
3
+ export * from './identity-sessions.js';
4
+ export * from './security-audit.js';
5
+ export * from './security-check.js';
6
+ export * from './security-probe.js';
7
+ export * from './source-checkout.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export * from './cookie-flags.js';
4
+ export * from './default-checks.js';
5
+ export * from './identity-sessions.js';
6
+ export * from './security-audit.js';
7
+ export * from './security-check.js';
8
+ export * from './security-probe.js';
9
+ export * from './source-checkout.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC;AAEtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { type EngineContext, type IdGenerator } from '@qa-ai-stlc/core';
2
+ import { type DefectDraft, type RelativePath, type SecurityAuditResult } from '@qa-ai-stlc/schemas';
3
+ import { type OpenIdentitySessionsOptions } from './identity-sessions.js';
4
+ import type { SecurityCheck } from './security-check.js';
5
+ export interface RunSecurityAuditOptions {
6
+ /** The check implementations to run; only classes the authorization names are used. */
7
+ readonly checks: readonly SecurityCheck[];
8
+ readonly idGenerator?: IdGenerator;
9
+ /** See `SecurityProbeOptions.pause`. */
10
+ readonly pause?: (milliseconds: number) => Promise<void>;
11
+ readonly requestTimeoutMs?: number;
12
+ /** See `OpenIdentitySessionsOptions`. */
13
+ readonly identities?: OpenIdentitySessionsOptions;
14
+ }
15
+ export interface SecurityAuditOutcome {
16
+ readonly result: SecurityAuditResult;
17
+ /** Where the engine registered the audit result; defect drafts cite it as evidence. */
18
+ readonly evidencePath: RelativePath;
19
+ /** One tracker-neutral draft per finding, none accepted: acceptance is a separate gate. */
20
+ readonly drafts: readonly DefectDraft[];
21
+ }
22
+ /**
23
+ * Runs the authorized black-box security checks and registers what they found. It refuses to start
24
+ * without an approved, current authorization (`assertSecurityAuthorized`), then gives every check
25
+ * the same probe, which holds the authorized limits. A check the authorization lists but this
26
+ * version cannot run is `skipped`, never silently omitted or reported `passed`; a check that
27
+ * could not complete is `blocked`, and the audit is then `partial` rather than a clean bill.
28
+ */
29
+ export declare function runSecurityAudit(context: EngineContext, options: RunSecurityAuditOptions): Promise<SecurityAuditOutcome>;
30
+ //# sourceMappingURL=security-audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-audit.d.ts","sourceRoot":"","sources":["../src/security-audit.ts"],"names":[],"mappings":"AAGA,OAAO,EAUL,KAAK,aAAa,EAClB,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EAIzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGL,KAAK,2BAA2B,EACjC,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,qBAAqB,CAAC;AAI7E,MAAM,WAAW,uBAAuB;IACtC,uFAAuF;IACvF,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IACnC,wCAAwC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,yCAAyC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,2BAA2B,CAAC;CACnD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,uFAAuF;IACvF,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;CACzC;AAYD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAmD/B"}
@@ -0,0 +1,173 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { EvidenceStore, ManifestStore, QaError, QaStore, assertSecurityAuthorized, loadConfig, randomIdGenerator, securityFindingToDefectDraft, toCanonicalJson, } from '@qa-ai-stlc/core';
4
+ import { CODE_ASSISTED_CHECK_CLASSES, SECURITY_CHECK_CLASSES, } from '@qa-ai-stlc/schemas';
5
+ import { NO_IDENTITY_SESSIONS, openIdentitySessions, } from './identity-sessions.js';
6
+ import { SecurityProbe } from './security-probe.js';
7
+ import { SourceCheckout } from './source-checkout.js';
8
+ function isCodeAssisted(checkClass) {
9
+ return CODE_ASSISTED_CHECK_CLASSES.includes(checkClass);
10
+ }
11
+ function skipped(checkClass, note) {
12
+ return { checkClass, status: 'skipped', note, findingIds: [] };
13
+ }
14
+ /**
15
+ * Runs the authorized black-box security checks and registers what they found. It refuses to start
16
+ * without an approved, current authorization (`assertSecurityAuthorized`), then gives every check
17
+ * the same probe, which holds the authorized limits. A check the authorization lists but this
18
+ * version cannot run is `skipped`, never silently omitted or reported `passed`; a check that
19
+ * could not complete is `blocked`, and the audit is then `partial` rather than a clean bill.
20
+ */
21
+ export async function runSecurityAudit(context, options) {
22
+ const authorization = await assertSecurityAuthorized(context);
23
+ const config = await loadConfig(context);
24
+ const idGenerator = options.idGenerator ?? randomIdGenerator;
25
+ const auditId = `audit-${idGenerator.next()}`;
26
+ const startedAt = context.clock.now().toISOString();
27
+ const probe = new SecurityProbe({
28
+ httpClient: context.httpClient,
29
+ authorization,
30
+ tlsInsecure: config.environments[authorization.environment.name]?.tlsInsecure === true,
31
+ ...(options.pause !== undefined ? { pause: options.pause } : {}),
32
+ ...(options.requestTimeoutMs !== undefined ? { requestTimeoutMs: options.requestTimeoutMs } : {}),
33
+ });
34
+ const identities = authorization.identities.length > 0
35
+ ? await openIdentitySessions(context, authorization, options.identities)
36
+ : NO_IDENTITY_SESSIONS;
37
+ const source = await openSourceCheckout(context, config.source?.path, authorization);
38
+ const { checks, findings, stoppedReason } = await runChecks(context, authorization, { probe, identities, ...(source.checkout !== undefined ? { source: source.checkout } : {}) }, options.checks, source.unavailableReason);
39
+ const isClean = stoppedReason === undefined && checks.every((entry) => entry.status !== 'blocked');
40
+ const result = {
41
+ schemaVersion: 1,
42
+ auditId,
43
+ environment: authorization.environment.name,
44
+ startedAt,
45
+ finishedAt: context.clock.now().toISOString(),
46
+ status: isClean ? 'completed' : 'partial',
47
+ ...(stoppedReason !== undefined ? { stoppedReason } : {}),
48
+ checks,
49
+ findings,
50
+ requests: probe.log,
51
+ };
52
+ assertFindingsCiteRequests(result);
53
+ const evidencePath = await registerAuditResult(context, result, idGenerator);
54
+ const drafts = findings.map((finding) => securityFindingToDefectDraft(finding, {
55
+ environment: result.environment,
56
+ evidencePaths: [evidencePath],
57
+ createdAt: result.finishedAt,
58
+ }));
59
+ return { result, evidencePath, drafts };
60
+ }
61
+ // The code-assisted level needs `source.path`; without it the audit still runs its black-box checks
62
+ // and says so, rather than failing a request the operator may have made for the whole set.
63
+ async function openSourceCheckout(context, sourcePath, authorization) {
64
+ const requested = CODE_ASSISTED_CHECK_CLASSES.filter((checkClass) => authorization.checks.includes(checkClass));
65
+ if (requested.length === 0) {
66
+ return {};
67
+ }
68
+ const checkout = sourcePath === undefined
69
+ ? undefined
70
+ : new SourceCheckout({
71
+ projectRoot: context.projectRoot,
72
+ relativeRoot: sourcePath,
73
+ fs: context.fs,
74
+ processRunner: context.processRunner,
75
+ });
76
+ const unavailableReason = checkout === undefined
77
+ ? 'source.path is not configured'
78
+ : (await context.fs.pathExists(checkout.absoluteDirectory(checkout.relativeRoot)))
79
+ ? undefined
80
+ : `source.path "${sourcePath ?? ''}" does not exist`;
81
+ if (unavailableReason !== undefined) {
82
+ context.logger.info('Code-assisted security checks are skipped; the audit runs black-box only', {
83
+ reason: unavailableReason,
84
+ checks: requested,
85
+ });
86
+ return { unavailableReason };
87
+ }
88
+ return { ...(checkout !== undefined ? { checkout } : {}) };
89
+ }
90
+ async function runChecks(context, authorization, checkInput, implementations, codeAssistedUnavailableReason) {
91
+ const entries = [];
92
+ const findings = [];
93
+ let stoppedReason;
94
+ for (const checkClass of SECURITY_CHECK_CLASSES) {
95
+ if (!authorization.checks.includes(checkClass)) {
96
+ continue;
97
+ }
98
+ if (stoppedReason !== undefined) {
99
+ entries.push(skipped(checkClass, `Not run: ${stoppedReason}.`));
100
+ continue;
101
+ }
102
+ if (codeAssistedUnavailableReason !== undefined && isCodeAssisted(checkClass)) {
103
+ entries.push(skipped(checkClass, `Not run: ${codeAssistedUnavailableReason}, so only the black-box checks ran.`));
104
+ continue;
105
+ }
106
+ const implementation = implementations.find((candidate) => candidate.checkClass === checkClass);
107
+ if (implementation === undefined) {
108
+ entries.push(skipped(checkClass, 'This version has no implementation of the check.'));
109
+ continue;
110
+ }
111
+ try {
112
+ // Only the code-assisted checks are given the checkout: a black-box check has no business reading code.
113
+ const { source, ...blackBoxInput } = checkInput;
114
+ const outcome = await implementation.run({
115
+ ...blackBoxInput,
116
+ authorization,
117
+ ...(source !== undefined && isCodeAssisted(checkClass) ? { source } : {}),
118
+ });
119
+ for (const finding of outcome.findings) {
120
+ if (finding.checkClass !== checkClass || findings.some((known) => known.id === finding.id)) {
121
+ throw new QaError('SECURITY_FINDING_INVALID', `The "${checkClass}" check returned finding "${finding.id}" of class "${finding.checkClass}", or reused an id`, { remediation: 'A check reports only its own class, with ids unique across the audit.' });
122
+ }
123
+ findings.push(finding);
124
+ }
125
+ const status = outcome.findings.length > 0 ? 'failed' : outcome.status;
126
+ entries.push({
127
+ checkClass,
128
+ status,
129
+ ...(outcome.note !== undefined ? { note: outcome.note } : {}),
130
+ findingIds: outcome.findings.map((finding) => finding.id),
131
+ });
132
+ }
133
+ catch (error) {
134
+ if (!(error instanceof QaError)) {
135
+ throw error;
136
+ }
137
+ context.logger.error('A security check could not complete', { code: error.code, checkClass });
138
+ entries.push({ checkClass, status: 'blocked', note: error.message, findingIds: [] });
139
+ if (error.code === 'SECURITY_BUDGET_EXHAUSTED') {
140
+ stoppedReason = 'the authorized request budget was used up';
141
+ }
142
+ }
143
+ }
144
+ return { checks: entries, findings, ...(stoppedReason !== undefined ? { stoppedReason } : {}) };
145
+ }
146
+ // A finding that cites a request the audit never made would point a reviewer at nothing.
147
+ function assertFindingsCiteRequests(result) {
148
+ for (const finding of result.findings) {
149
+ const invalid = finding.requestIndexes.filter((index) => index >= result.requests.length);
150
+ if (invalid.length > 0) {
151
+ throw new QaError('SECURITY_FINDING_INVALID', `Finding "${finding.id}" cites request(s) ${invalid.join(', ')} that the audit did not make`, { remediation: 'A finding cites positions in the audit request log.' });
152
+ }
153
+ }
154
+ }
155
+ async function registerAuditResult(context, result, idGenerator) {
156
+ const store = new QaStore({ projectRoot: context.projectRoot, fs: context.fs });
157
+ const manifest = new ManifestStore({ store, clock: context.clock });
158
+ const evidenceStore = new EvidenceStore({ store, manifest, clock: context.clock });
159
+ const registration = await evidenceStore.register({
160
+ id: `evidence-${idGenerator.next()}`,
161
+ runId: `security-${result.auditId}`,
162
+ kind: 'other',
163
+ fileExtension: 'json',
164
+ content: toCanonicalJson(result),
165
+ });
166
+ if (registration.status === 'quarantined') {
167
+ throw new QaError('SECURITY_EVIDENCE_QUARANTINED', `The audit result matched ${registration.patterns.join(', ')} and was quarantined instead of registered`, {
168
+ remediation: `A sanitized receipt is at ${registration.receiptPath}. Remove the secret from the application's responses.`,
169
+ });
170
+ }
171
+ return registration.evidence.path;
172
+ }
173
+ //# sourceMappingURL=security-audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-audit.js","sourceRoot":"","sources":["../src/security-audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { SecurityAuthorization, SecurityCheckClass, SecurityCheckStatus, SecurityFinding } from '@qa-ai-stlc/schemas';
2
+ import type { IdentitySessions } from './identity-sessions.js';
3
+ import type { SecurityProbe } from './security-probe.js';
4
+ import type { SourceCheckout } from './source-checkout.js';
5
+ export interface SecurityCheckInput {
6
+ /** The only way a check reaches the application; every authorized limit is enforced inside it. */
7
+ readonly probe: SecurityProbe;
8
+ readonly authorization: SecurityAuthorization;
9
+ /** The identities the authorization names, signed in; empty when it names none. */
10
+ readonly identities: IdentitySessions;
11
+ /** The source checkout (`source.path`), read-only; absent when none is configured. */
12
+ readonly source?: SourceCheckout;
13
+ }
14
+ export interface SecurityCheckOutcome {
15
+ /**
16
+ * `passed` only when the check looked and found nothing. A check that could not look says
17
+ * `skipped` (not applicable or not enough authorization) or `blocked` (something stopped it),
18
+ * and one unsure of what it saw says `uncertain`: none of these is ever reported as `passed`.
19
+ */
20
+ readonly status: Exclude<SecurityCheckStatus, 'failed'>;
21
+ readonly note?: string;
22
+ /** Any finding makes the check `failed`, whatever `status` says. */
23
+ readonly findings: readonly SecurityFinding[];
24
+ }
25
+ /** One class of black-box checks (development plan 2.7), run through the probe. */
26
+ export interface SecurityCheck {
27
+ readonly checkClass: SecurityCheckClass;
28
+ run(input: SecurityCheckInput): Promise<SecurityCheckOutcome>;
29
+ }
30
+ //# sourceMappingURL=security-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-check.d.ts","sourceRoot":"","sources":["../src/security-check.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,WAAW,kBAAkB;IACjC,kGAAkG;IAClG,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC;IAC9C,mFAAmF;IACnF,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,sFAAsF;IACtF,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;CAC/C;AAED,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,GAAG,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC/D"}
@@ -0,0 +1,4 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export {};
4
+ //# sourceMappingURL=security-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-check.js","sourceRoot":"","sources":["../src/security-check.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,sCAAsC"}
@@ -0,0 +1,56 @@
1
+ import { type HttpClient } from '@qa-ai-stlc/core';
2
+ import type { SecurityAuthorization, SecurityRequestLogEntry } from '@qa-ai-stlc/schemas';
3
+ export interface ProbeRequest {
4
+ readonly method: string;
5
+ /** An absolute path on the audited host, such as `/admin/users`; never a full URL. */
6
+ readonly path: string;
7
+ readonly headers?: Readonly<Record<string, string>>;
8
+ readonly body?: string;
9
+ }
10
+ export interface ProbeResponse {
11
+ readonly status: number;
12
+ readonly headers: Readonly<Record<string, string>>;
13
+ readonly setCookies: readonly string[];
14
+ /** Capped; page-controlled, so untrusted. */
15
+ readonly bodyText: string;
16
+ /** Position of this request in the probe's log, for a finding to cite. */
17
+ readonly requestIndex: number;
18
+ }
19
+ export interface SecurityProbeOptions {
20
+ readonly httpClient: HttpClient;
21
+ readonly authorization: SecurityAuthorization;
22
+ /** Whether the environment's configuration turns off certificate validation (ADR-011). */
23
+ readonly tlsInsecure?: boolean;
24
+ readonly requestTimeoutMs?: number;
25
+ /** Waits between requests to honour the rate limit; injected so tests run without real delay. */
26
+ readonly pause?: (milliseconds: number) => Promise<void>;
27
+ readonly nowMs?: () => number;
28
+ }
29
+ /**
30
+ * The one way an audit reaches the network. Every limit the operator authorized is enforced here
31
+ * and nowhere else, so no check can bypass it (AGENTS.md 12.7): a request is sent only to the
32
+ * authorized origin and allowlist, a method that is not a read only when the authorization names
33
+ * that exact method and path, never faster than the rate limit, and never beyond the request
34
+ * budget. Redirects are not followed: each hop would have to pass the same checks. Every request,
35
+ * sent or refused, is logged for the audit result.
36
+ */
37
+ export declare class SecurityProbe {
38
+ readonly log: SecurityRequestLogEntry[];
39
+ private sentCount;
40
+ private lastSentAtMs;
41
+ private readonly httpClient;
42
+ private readonly authorization;
43
+ private readonly tlsInsecure;
44
+ private readonly requestTimeoutMs;
45
+ private readonly pause;
46
+ private readonly nowMs;
47
+ constructor(options: SecurityProbeOptions);
48
+ request(request: ProbeRequest): Promise<ProbeResponse>;
49
+ /** Whether the audit has used up its authorized request budget. */
50
+ get isBudgetExhausted(): boolean;
51
+ private resolve;
52
+ private refuseUnlessPermitted;
53
+ private refuse;
54
+ private waitForRateLimit;
55
+ }
56
+ //# sourceMappingURL=security-probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-probe.d.ts","sourceRoot":"","sources":["../src/security-probe.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAM1F,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC;IAC9C,0FAA0F;IAC1F,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,iGAAiG;IACjG,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CAC/B;AAYD;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,QAAQ,CAAC,GAAG,EAAE,uBAAuB,EAAE,CAAM;IAC7C,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwB;IACtD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0C;IAChE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,OAAO,EAAE,oBAAoB;IASnC,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IA+C5D,mEAAmE;IACnE,IAAI,iBAAiB,IAAI,OAAO,CAE/B;IAED,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,MAAM;YAaA,gBAAgB;CAU/B"}
@@ -0,0 +1,128 @@
1
+ // Copyright The QA-AI-STLC Authors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { QaError, isUrlAllowed } from '@qa-ai-stlc/core';
4
+ const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
5
+ const MAX_BODY_CHARACTERS = 64 * 1024;
6
+ const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
7
+ function realPause(milliseconds) {
8
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
9
+ }
10
+ /** Where a request went, without query values (they can carry tokens) or credentials in the URL. */
11
+ function describeUrl(url) {
12
+ const names = [...url.searchParams.keys()];
13
+ return `${url.origin}${url.pathname}${names.length > 0 ? `?${names.join('&')}` : ''}`;
14
+ }
15
+ /**
16
+ * The one way an audit reaches the network. Every limit the operator authorized is enforced here
17
+ * and nowhere else, so no check can bypass it (AGENTS.md 12.7): a request is sent only to the
18
+ * authorized origin and allowlist, a method that is not a read only when the authorization names
19
+ * that exact method and path, never faster than the rate limit, and never beyond the request
20
+ * budget. Redirects are not followed: each hop would have to pass the same checks. Every request,
21
+ * sent or refused, is logged for the audit result.
22
+ */
23
+ export class SecurityProbe {
24
+ log = [];
25
+ sentCount = 0;
26
+ lastSentAtMs;
27
+ httpClient;
28
+ authorization;
29
+ tlsInsecure;
30
+ requestTimeoutMs;
31
+ pause;
32
+ nowMs;
33
+ constructor(options) {
34
+ this.httpClient = options.httpClient;
35
+ this.authorization = options.authorization;
36
+ this.tlsInsecure = options.tlsInsecure === true;
37
+ this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
38
+ this.pause = options.pause ?? realPause;
39
+ this.nowMs = options.nowMs ?? Date.now;
40
+ }
41
+ async request(request) {
42
+ const method = request.method.toUpperCase();
43
+ const url = this.resolve(method, request.path);
44
+ this.refuseUnlessPermitted(method, request.path, url);
45
+ if (this.sentCount >= this.authorization.rateLimit.maxRequests) {
46
+ throw new QaError('SECURITY_BUDGET_EXHAUSTED', `The audit sent its authorized ${String(this.authorization.rateLimit.maxRequests)} requests and stopped`, { remediation: 'Authorize a larger request budget if the operator wants a longer audit.' });
47
+ }
48
+ await this.waitForRateLimit();
49
+ const requestIndex = this.log.length;
50
+ this.sentCount += 1;
51
+ try {
52
+ const response = await this.httpClient.request(url.href, {
53
+ method,
54
+ ...(request.headers !== undefined ? { headers: request.headers } : {}),
55
+ ...(request.body !== undefined ? { body: request.body } : {}),
56
+ redirect: 'manual',
57
+ signal: AbortSignal.timeout(this.requestTimeoutMs),
58
+ ...(this.tlsInsecure ? { tlsInsecure: true } : {}),
59
+ });
60
+ this.log.push({ method, url: describeUrl(url), outcome: 'sent', status: response.status });
61
+ return {
62
+ status: response.status,
63
+ headers: response.headers,
64
+ setCookies: response.setCookies,
65
+ bodyText: response.bodyText.slice(0, MAX_BODY_CHARACTERS),
66
+ requestIndex,
67
+ };
68
+ }
69
+ catch (error) {
70
+ this.log.push({
71
+ method,
72
+ url: describeUrl(url),
73
+ outcome: 'sent',
74
+ reason: 'the request failed before a response arrived',
75
+ });
76
+ throw new QaError('SECURITY_REQUEST_FAILED', `${method} ${describeUrl(url)} failed`, {
77
+ cause: error,
78
+ remediation: 'Check that the environment is reachable, then run the audit again.',
79
+ });
80
+ }
81
+ }
82
+ /** Whether the audit has used up its authorized request budget. */
83
+ get isBudgetExhausted() {
84
+ return this.sentCount >= this.authorization.rateLimit.maxRequests;
85
+ }
86
+ resolve(method, path) {
87
+ // A path that is really a URL would let the host part escape the authorized origin.
88
+ if (!path.startsWith('/') || path.startsWith('//')) {
89
+ this.refuse(method, path, 'not an absolute path on the audited host');
90
+ }
91
+ return new URL(path, this.authorization.environment.baseUrl);
92
+ }
93
+ refuseUnlessPermitted(method, path, url) {
94
+ const { baseUrl, allowlist } = this.authorization.environment;
95
+ if (!isUrlAllowed(url.href, allowlist, baseUrl)) {
96
+ this.refuse(method, path, 'outside the authorized origin and allowlist', url);
97
+ }
98
+ if (SAFE_METHODS.has(method)) {
99
+ return;
100
+ }
101
+ const isAuthorizedMutation = this.authorization.allowedMutations.some((mutation) => mutation.method === method && mutation.path === url.pathname);
102
+ if (!isAuthorizedMutation) {
103
+ this.refuse(method, path, 'not a read and not an authorized mutation', url);
104
+ }
105
+ }
106
+ refuse(method, path, reason, url) {
107
+ this.log.push({
108
+ method,
109
+ url: url === undefined ? path : describeUrl(url),
110
+ outcome: 'refused',
111
+ reason,
112
+ });
113
+ throw new QaError('SECURITY_REQUEST_REFUSED', `${method} ${path} was refused: ${reason}`, {
114
+ remediation: 'An audit only sends reads and the mutations its authorization names, to the authorized origin.',
115
+ });
116
+ }
117
+ async waitForRateLimit() {
118
+ const intervalMs = 1000 / this.authorization.rateLimit.requestsPerSecond;
119
+ if (this.lastSentAtMs !== undefined) {
120
+ const waitMs = this.lastSentAtMs + intervalMs - this.nowMs();
121
+ if (waitMs > 0) {
122
+ await this.pause(waitMs);
123
+ }
124
+ }
125
+ this.lastSentAtMs = this.nowMs();
126
+ }
127
+ }
128
+ //# sourceMappingURL=security-probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-probe.js","sourceRoot":"","sources":["../src/security-probe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { type FileSystem, type ProcessRunner } from '@qa-ai-stlc/core';
2
+ import type { RelativePath } from '@qa-ai-stlc/schemas';
3
+ export interface SourceFile {
4
+ /** Project-relative, with `/` separators, so a finding can cite it as it stands. */
5
+ readonly path: RelativePath;
6
+ readonly text: string;
7
+ }
8
+ export interface SourceScan {
9
+ readonly files: readonly SourceFile[];
10
+ /** True when the file limit stopped the scan, so a clean result does not cover the whole checkout. */
11
+ readonly isTruncated: boolean;
12
+ /** Files left out because they are binary or larger than the size limit. */
13
+ readonly skippedCount: number;
14
+ }
15
+ export interface SourceCheckoutOptions {
16
+ readonly projectRoot: string;
17
+ /** The configured `source.path`; `.` and `./app` forms are accepted. */
18
+ readonly relativeRoot: string;
19
+ readonly fs: FileSystem;
20
+ readonly processRunner: ProcessRunner;
21
+ readonly maxFiles?: number;
22
+ readonly maxFileSizeBytes?: number;
23
+ }
24
+ export declare const DEFAULT_MAX_SOURCE_FILES = 3000;
25
+ export declare const DEFAULT_MAX_SOURCE_FILE_SIZE_BYTES: number;
26
+ /**
27
+ * A read-only view of the application's source checkout (`source.path`). It exposes no write, so a
28
+ * code-assisted check cannot change the code it audits (AGENTS.md 12.4). The scan is bounded by a
29
+ * file count and a file size and says when it was cut short, so a clean result is never mistaken
30
+ * for a complete one.
31
+ */
32
+ export declare class SourceCheckout {
33
+ /** Project-relative with `/` separators; `''` when the checkout is the project root itself. */
34
+ readonly relativeRoot: string;
35
+ readonly processRunner: ProcessRunner;
36
+ private readonly projectRoot;
37
+ private readonly fs;
38
+ private readonly maxFiles;
39
+ private readonly maxFileSizeBytes;
40
+ constructor(options: SourceCheckoutOptions);
41
+ /** The text files of the checkout in a stable order, without installed or generated directories. */
42
+ scan(): Promise<SourceScan>;
43
+ /** One file's text, or `undefined` when it is missing, binary or over the size limit. */
44
+ readText(path: RelativePath): Promise<string | undefined>;
45
+ /**
46
+ * The nearest directory, from the checkout's root up to the project root, that holds `fileName`:
47
+ * project-relative, `''` for the project root itself. A monorepo keeps one lockfile above the
48
+ * packages it covers.
49
+ */
50
+ findDirectoryWith(fileName: string): Promise<string | undefined>;
51
+ /** The absolute, OS-native path of a project-relative directory (`''` is the project root). */
52
+ absoluteDirectory(relativeDirectory: string): string;
53
+ private isInsideRoot;
54
+ private isInIgnoredDirectory;
55
+ }
56
+ //# sourceMappingURL=source-checkout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-checkout.d.ts","sourceRoot":"","sources":["../src/source-checkout.ts"],"names":[],"mappings":"AAIA,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,UAAU;IACzB,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,sGAAsG;IACtG,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,kCAAkC,QAAa,CAAC;AAiD7D;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,+FAA+F;IAC/F,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,OAAO,EAAE,qBAAqB;IAU1C,oGAAoG;IAC9F,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAoBjC,yFAAyF;IACnF,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAe/D;;;;OAIG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAetE,+FAA+F;IAC/F,iBAAiB,CAAC,iBAAiB,EAAE,MAAM,GAAG,MAAM;IAMpD,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,oBAAoB;CAM7B"}