oauthlint 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/bin/oauthlint.js +5 -0
- package/dist/adapters/semgrep.d.ts +36 -0
- package/dist/adapters/semgrep.d.ts.map +1 -0
- package/dist/adapters/semgrep.js +156 -0
- package/dist/adapters/semgrep.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +103 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +69 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +51 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +32 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/scan.d.ts +24 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +73 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +40 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/reporter.d.ts +17 -0
- package/dist/core/reporter.d.ts.map +1 -0
- package/dist/core/reporter.js +111 -0
- package/dist/core/reporter.js.map +1 -0
- package/dist/core/sarif.d.ts +69 -0
- package/dist/core/sarif.d.ts.map +1 -0
- package/dist/core/sarif.js +115 -0
- package/dist/core/sarif.js.map +1 -0
- package/dist/core/severity.d.ts +23 -0
- package/dist/core/severity.d.ts.map +1 -0
- package/dist/core/severity.js +42 -0
- package/dist/core/severity.js.map +1 -0
- package/dist/core/suppress.d.ts +40 -0
- package/dist/core/suppress.d.ts.map +1 -0
- package/dist/core/suppress.js +81 -0
- package/dist/core/suppress.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAA4B,MAAM,wBAAwB,CAAC;AAMlF,OAAO,KAAK,EAAW,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gDAAgD;IAChD,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,GAAG,KAAK,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,8CAA8C;IAC9C,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;CAChC;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0DvE"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { RULES_ROOT } from 'oauthlint-rules';
|
|
3
|
+
import { SemgrepAdapter, SemgrepNotInstalledError } from '../adapters/semgrep.js';
|
|
4
|
+
import { loadConfig } from '../core/config.js';
|
|
5
|
+
import { Reporter } from '../core/reporter.js';
|
|
6
|
+
import { toSarif } from '../core/sarif.js';
|
|
7
|
+
import { exitCodeFor, highestSeverity, meetsThreshold } from '../core/severity.js';
|
|
8
|
+
import { applySuppressions } from '../core/suppress.js';
|
|
9
|
+
export async function runScan(opts) {
|
|
10
|
+
const target = resolve(opts.path);
|
|
11
|
+
const config = await loadConfig(target);
|
|
12
|
+
const rulesDir = opts.rulesDir ?? config.customRulesDir ?? RULES_ROOT;
|
|
13
|
+
const failOn = opts.failOn ?? config.failOn ?? 'HIGH';
|
|
14
|
+
const format = opts.format ?? (opts.json ? 'json' : 'pretty');
|
|
15
|
+
const stream = opts.stream ?? process.stdout;
|
|
16
|
+
const reporter = new Reporter({ json: format === 'json', stream });
|
|
17
|
+
if (format === 'pretty')
|
|
18
|
+
reporter.reportStart(target, await countRuleFiles(rulesDir));
|
|
19
|
+
const adapter = opts.adapter ?? new SemgrepAdapter({ configPath: rulesDir });
|
|
20
|
+
let result;
|
|
21
|
+
try {
|
|
22
|
+
result = await adapter.scan(target, { applyFixes: opts.fix });
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
if (err instanceof SemgrepNotInstalledError) {
|
|
26
|
+
(opts.stream ?? process.stderr).write(`${err.message}\n`);
|
|
27
|
+
return 127;
|
|
28
|
+
}
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
// Apply inline suppression directives first (they're source-truth)
|
|
32
|
+
// and then the severity filter on whatever survives.
|
|
33
|
+
let findings = result.findings;
|
|
34
|
+
const { kept, suppressed } = await applySuppressions(findings);
|
|
35
|
+
findings = kept;
|
|
36
|
+
const minSeverity = opts.severity;
|
|
37
|
+
if (minSeverity) {
|
|
38
|
+
findings = findings.filter((f) => meetsThreshold(f.severity, minSeverity));
|
|
39
|
+
}
|
|
40
|
+
if (format === 'sarif') {
|
|
41
|
+
const sarif = await toSarif({ ...result, findings });
|
|
42
|
+
stream.write(`${JSON.stringify(sarif, null, 2)}\n`);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
reporter.reportResult({ ...result, findings });
|
|
46
|
+
if (opts.fix && format === 'pretty') {
|
|
47
|
+
const fixed = findings.filter((f) => f.ruleId.startsWith('auth.cookie.')).length;
|
|
48
|
+
stream.write(`\n🛠 Auto-fix applied where possible — re-run \`oauthlint scan\` to confirm.${fixed > 0 ? ` (${fixed} cookie-* finding${fixed === 1 ? '' : 's'} eligible.)` : ''}\n`);
|
|
49
|
+
}
|
|
50
|
+
if (suppressed.length > 0 && format === 'pretty') {
|
|
51
|
+
stream.write(`\nℹ ${suppressed.length} finding${suppressed.length === 1 ? '' : 's'} suppressed via inline directives.\n`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (failOn === 'off')
|
|
55
|
+
return 0;
|
|
56
|
+
const worst = highestSeverity(findings);
|
|
57
|
+
if (worst === null)
|
|
58
|
+
return 0;
|
|
59
|
+
if (!meetsThreshold(worst, failOn))
|
|
60
|
+
return 0;
|
|
61
|
+
return exitCodeFor(worst);
|
|
62
|
+
}
|
|
63
|
+
async function countRuleFiles(dir) {
|
|
64
|
+
try {
|
|
65
|
+
const { loadAllRules } = await import('oauthlint-rules');
|
|
66
|
+
const rules = await loadAllRules(dir);
|
|
67
|
+
return rules.length;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,eAAe,EAAc,MAAM,aAAa,CAAC;AAa/D,eAAO,MAAM,cAAc,EAAE,eAE5B,CAAC;AAEF,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAwBtE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { cosmiconfig } from 'cosmiconfig';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { SEVERITIES } from '../types.js';
|
|
4
|
+
const SeverityValue = z.enum(SEVERITIES);
|
|
5
|
+
const ConfigSchema = z.object({
|
|
6
|
+
version: z.number().default(1),
|
|
7
|
+
include: z.array(z.string()).optional(),
|
|
8
|
+
exclude: z.array(z.string()).optional(),
|
|
9
|
+
rules: z.record(z.union([z.literal('off'), z.literal('warn'), SeverityValue])).optional(),
|
|
10
|
+
customRulesDir: z.string().optional(),
|
|
11
|
+
failOn: z.union([SeverityValue, z.literal('off')]).default('HIGH'),
|
|
12
|
+
});
|
|
13
|
+
export const DEFAULT_CONFIG = {
|
|
14
|
+
failOn: 'HIGH',
|
|
15
|
+
};
|
|
16
|
+
export async function loadConfig(cwd) {
|
|
17
|
+
const explorer = cosmiconfig('oauthlint', {
|
|
18
|
+
searchPlaces: [
|
|
19
|
+
'.oauthlintrc',
|
|
20
|
+
'.oauthlintrc.json',
|
|
21
|
+
'.oauthlintrc.yml',
|
|
22
|
+
'.oauthlintrc.yaml',
|
|
23
|
+
'oauthlint.config.js',
|
|
24
|
+
'oauthlint.config.mjs',
|
|
25
|
+
'oauthlint.config.cjs',
|
|
26
|
+
'package.json',
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
const result = await explorer.search(cwd);
|
|
30
|
+
if (!result || result.isEmpty)
|
|
31
|
+
return DEFAULT_CONFIG;
|
|
32
|
+
const parsed = ConfigSchema.safeParse(result.config);
|
|
33
|
+
if (!parsed.success) {
|
|
34
|
+
throw new Error(`Invalid oauthlint config in ${result.filepath}:\n${parsed.error.issues
|
|
35
|
+
.map((i) => ` - ${i.path.join('.')}: ${i.message}`)
|
|
36
|
+
.join('\n')}`);
|
|
37
|
+
}
|
|
38
|
+
return parsed.data;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAwB,UAAU,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,aAAa,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAEzC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9B,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACvC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACvC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACzF,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;CACnE,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,MAAM,EAAE,MAAM;CACf,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW;IAC1C,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,EAAE;QACxC,YAAY,EAAE;YACZ,cAAc;YACd,mBAAmB;YACnB,kBAAkB;YAClB,mBAAmB;YACnB,qBAAqB;YACrB,sBAAsB;YACtB,sBAAsB;YACtB,cAAc;SACf;KACF,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,cAAc,CAAC;IACrD,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,+BAA+B,MAAM,CAAC,QAAQ,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM;aACpE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aACnD,IAAI,CAAC,IAAI,CAAC,EAAE,CAChB,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ScanResult } from '../types.js';
|
|
2
|
+
export interface ReporterOptions {
|
|
3
|
+
/** When true, emit machine-readable JSON instead of pretty output. */
|
|
4
|
+
json?: boolean;
|
|
5
|
+
/** Stream to write to (defaults to process.stdout). */
|
|
6
|
+
stream?: NodeJS.WritableStream;
|
|
7
|
+
}
|
|
8
|
+
export declare class Reporter {
|
|
9
|
+
private readonly json;
|
|
10
|
+
private readonly stream;
|
|
11
|
+
constructor(opts?: ReporterOptions);
|
|
12
|
+
reportStart(target: string, ruleCount: number): void;
|
|
13
|
+
reportResult(result: ScanResult): void;
|
|
14
|
+
private printFinding;
|
|
15
|
+
private line;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/core/reporter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAW,UAAU,EAAgB,MAAM,aAAa,CAAC;AAUrE,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;CAChC;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;gBAEnC,IAAI,GAAE,eAAoB;IAKtC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAOpD,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAiFtC,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,IAAI;CAGb"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
const SEVERITY_COLOR = {
|
|
3
|
+
CRITICAL: (s) => pc.bgRed(pc.white(s)),
|
|
4
|
+
HIGH: pc.red,
|
|
5
|
+
MEDIUM: pc.yellow,
|
|
6
|
+
LOW: pc.cyan,
|
|
7
|
+
INFO: pc.gray,
|
|
8
|
+
};
|
|
9
|
+
export class Reporter {
|
|
10
|
+
json;
|
|
11
|
+
stream;
|
|
12
|
+
constructor(opts = {}) {
|
|
13
|
+
this.json = opts.json ?? false;
|
|
14
|
+
this.stream = opts.stream ?? process.stdout;
|
|
15
|
+
}
|
|
16
|
+
reportStart(target, ruleCount) {
|
|
17
|
+
if (this.json)
|
|
18
|
+
return;
|
|
19
|
+
this.line(pc.dim(`OAuthLint — scanning ${pc.bold(target)}`));
|
|
20
|
+
this.line(pc.dim(`Loaded ${ruleCount} rules`));
|
|
21
|
+
this.line('');
|
|
22
|
+
}
|
|
23
|
+
reportResult(result) {
|
|
24
|
+
if (this.json) {
|
|
25
|
+
const payload = {
|
|
26
|
+
schemaVersion: 'oauthlint-v1',
|
|
27
|
+
scannedFiles: result.scannedFiles,
|
|
28
|
+
durationMs: result.durationMs,
|
|
29
|
+
semgrepVersion: result.semgrepVersion,
|
|
30
|
+
errors: result.errors,
|
|
31
|
+
findings: result.findings.map((f) => ({
|
|
32
|
+
ruleId: f.ruleId,
|
|
33
|
+
oauthlintRuleId: f.oauthlintRuleId,
|
|
34
|
+
severity: f.severity,
|
|
35
|
+
filePath: f.filePath,
|
|
36
|
+
startLine: f.startLine,
|
|
37
|
+
endLine: f.endLine,
|
|
38
|
+
message: f.message,
|
|
39
|
+
docUrl: f.docUrl,
|
|
40
|
+
cwe: f.cwe,
|
|
41
|
+
llmPrevalence: f.llmPrevalence,
|
|
42
|
+
})),
|
|
43
|
+
};
|
|
44
|
+
this.line(JSON.stringify(payload, null, 2));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const sep = pc.dim('━'.repeat(60));
|
|
48
|
+
if (result.findings.length === 0) {
|
|
49
|
+
this.line(pc.green('✓ No auth issues found.'));
|
|
50
|
+
this.line(pc.dim(`Scanned ${result.scannedFiles} file${result.scannedFiles === 1 ? '' : 's'} in ${result.durationMs}ms.`));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const counts = countBySeverity(result.findings);
|
|
54
|
+
const summary = [
|
|
55
|
+
counts.CRITICAL ? pc.red(`${counts.CRITICAL} critical`) : null,
|
|
56
|
+
counts.HIGH ? pc.red(`${counts.HIGH} high`) : null,
|
|
57
|
+
counts.MEDIUM ? pc.yellow(`${counts.MEDIUM} medium`) : null,
|
|
58
|
+
counts.LOW ? pc.cyan(`${counts.LOW} low`) : null,
|
|
59
|
+
counts.INFO ? pc.gray(`${counts.INFO} info`) : null,
|
|
60
|
+
]
|
|
61
|
+
.filter((x) => x !== null)
|
|
62
|
+
.join(pc.dim(' · '));
|
|
63
|
+
this.line(sep);
|
|
64
|
+
this.line(pc.bold(`Found ${result.findings.length} issue${result.findings.length === 1 ? '' : 's'}: `) +
|
|
65
|
+
summary);
|
|
66
|
+
this.line('');
|
|
67
|
+
const sorted = [...result.findings].sort((a, b) => {
|
|
68
|
+
const order = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO'];
|
|
69
|
+
return order.indexOf(a.severity) - order.indexOf(b.severity);
|
|
70
|
+
});
|
|
71
|
+
for (const f of sorted) {
|
|
72
|
+
this.printFinding(f);
|
|
73
|
+
}
|
|
74
|
+
this.line(sep);
|
|
75
|
+
this.line(pc.dim(`Scanned ${result.scannedFiles} files in ${result.durationMs}ms${result.semgrepVersion ? ` · semgrep ${result.semgrepVersion}` : ''}`));
|
|
76
|
+
if (result.errors.length > 0) {
|
|
77
|
+
this.line(pc.yellow(`⚠ ${result.errors.length} semgrep error${result.errors.length === 1 ? '' : 's'}:`));
|
|
78
|
+
for (const e of result.errors) {
|
|
79
|
+
this.line(pc.dim(` · ${e}`));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
printFinding(f) {
|
|
84
|
+
const tag = SEVERITY_COLOR[f.severity](` ${f.severity} `);
|
|
85
|
+
const id = pc.bold(f.ruleId);
|
|
86
|
+
this.line(`${tag} ${id}${f.oauthlintRuleId ? pc.dim(` (${f.oauthlintRuleId})`) : ''}`);
|
|
87
|
+
this.line(pc.dim(` ${f.filePath}:${f.startLine}`));
|
|
88
|
+
const firstLine = f.message.split('\n')[0]?.trim() ?? '';
|
|
89
|
+
if (firstLine)
|
|
90
|
+
this.line(pc.dim(' → ') + firstLine);
|
|
91
|
+
if (f.docUrl)
|
|
92
|
+
this.line(pc.dim(` 📖 ${f.docUrl}`));
|
|
93
|
+
this.line('');
|
|
94
|
+
}
|
|
95
|
+
line(s = '') {
|
|
96
|
+
this.stream.write(`${s}\n`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function countBySeverity(findings) {
|
|
100
|
+
const counts = {
|
|
101
|
+
CRITICAL: 0,
|
|
102
|
+
HIGH: 0,
|
|
103
|
+
MEDIUM: 0,
|
|
104
|
+
LOW: 0,
|
|
105
|
+
INFO: 0,
|
|
106
|
+
};
|
|
107
|
+
for (const f of findings)
|
|
108
|
+
counts[f.severity]++;
|
|
109
|
+
return counts;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/core/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ScanResult } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Convert OAuthLint scan results into SARIF v2.1.0.
|
|
4
|
+
*
|
|
5
|
+
* Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html
|
|
6
|
+
*
|
|
7
|
+
* GitHub Code Scanning consumes SARIF directly via the
|
|
8
|
+
* `github/codeql-action/upload-sarif` action, which is the integration
|
|
9
|
+
* path most enterprise teams want.
|
|
10
|
+
*/
|
|
11
|
+
interface SarifReport {
|
|
12
|
+
$schema: string;
|
|
13
|
+
version: string;
|
|
14
|
+
runs: SarifRun[];
|
|
15
|
+
}
|
|
16
|
+
interface SarifRun {
|
|
17
|
+
tool: {
|
|
18
|
+
driver: SarifDriver;
|
|
19
|
+
};
|
|
20
|
+
results: SarifResult[];
|
|
21
|
+
}
|
|
22
|
+
interface SarifDriver {
|
|
23
|
+
name: string;
|
|
24
|
+
version: string;
|
|
25
|
+
informationUri: string;
|
|
26
|
+
rules: SarifRule[];
|
|
27
|
+
}
|
|
28
|
+
interface SarifRule {
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
shortDescription: {
|
|
32
|
+
text: string;
|
|
33
|
+
};
|
|
34
|
+
fullDescription: {
|
|
35
|
+
text: string;
|
|
36
|
+
};
|
|
37
|
+
helpUri?: string;
|
|
38
|
+
defaultConfiguration: {
|
|
39
|
+
level: 'note' | 'warning' | 'error';
|
|
40
|
+
};
|
|
41
|
+
properties: {
|
|
42
|
+
tags: string[];
|
|
43
|
+
precision?: string;
|
|
44
|
+
security_severity?: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
interface SarifResult {
|
|
48
|
+
ruleId: string;
|
|
49
|
+
level: 'note' | 'warning' | 'error';
|
|
50
|
+
message: {
|
|
51
|
+
text: string;
|
|
52
|
+
};
|
|
53
|
+
locations: SarifLocation[];
|
|
54
|
+
properties?: Record<string, unknown>;
|
|
55
|
+
}
|
|
56
|
+
interface SarifLocation {
|
|
57
|
+
physicalLocation: {
|
|
58
|
+
artifactLocation: {
|
|
59
|
+
uri: string;
|
|
60
|
+
};
|
|
61
|
+
region: {
|
|
62
|
+
startLine: number;
|
|
63
|
+
endLine: number;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export declare function toSarif(result: ScanResult): Promise<SarifReport>;
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=sarif.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../../src/core/sarif.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAW,UAAU,EAAgB,MAAM,aAAa,CAAC;AAErE;;;;;;;;GAQG;AAEH,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,QAAQ,EAAE,CAAC;CAClB;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE;QAAE,MAAM,EAAE,WAAW,CAAA;KAAE,CAAC;IAC9B,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,eAAe,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE;QAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;KAAE,CAAC;IAC9D,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAChF;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACpC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,UAAU,aAAa;IACrB,gBAAgB,EAAE;QAChB,gBAAgB,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;QAClC,MAAM,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;KAChD,CAAC;CACH;AAoCD,wBAAsB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAyBtE"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
const SARIF_LEVEL = {
|
|
5
|
+
INFO: 'note',
|
|
6
|
+
LOW: 'note',
|
|
7
|
+
MEDIUM: 'warning',
|
|
8
|
+
HIGH: 'error',
|
|
9
|
+
CRITICAL: 'error',
|
|
10
|
+
};
|
|
11
|
+
// GitHub uses a 0.0–10.0 numeric "security severity" to colour the
|
|
12
|
+
// inline annotations in the Security tab.
|
|
13
|
+
const SECURITY_SEVERITY = {
|
|
14
|
+
INFO: '1.0',
|
|
15
|
+
LOW: '3.0',
|
|
16
|
+
MEDIUM: '5.5',
|
|
17
|
+
HIGH: '7.5',
|
|
18
|
+
CRITICAL: '9.5',
|
|
19
|
+
};
|
|
20
|
+
async function readToolVersion() {
|
|
21
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
for (const candidate of [
|
|
23
|
+
resolve(here, '..', '..', 'package.json'),
|
|
24
|
+
resolve(here, '..', '..', '..', 'package.json'),
|
|
25
|
+
]) {
|
|
26
|
+
try {
|
|
27
|
+
const pkg = JSON.parse(await readFile(candidate, 'utf8'));
|
|
28
|
+
if (pkg.version)
|
|
29
|
+
return pkg.version;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
/* try next */
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return '0.0.0';
|
|
36
|
+
}
|
|
37
|
+
export async function toSarif(result) {
|
|
38
|
+
const version = await readToolVersion();
|
|
39
|
+
const ruleIndex = new Map();
|
|
40
|
+
for (const f of result.findings) {
|
|
41
|
+
if (ruleIndex.has(f.ruleId))
|
|
42
|
+
continue;
|
|
43
|
+
ruleIndex.set(f.ruleId, buildRule(f));
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
$schema: 'https://json.schemastore.org/sarif-2.1.0.json',
|
|
47
|
+
version: '2.1.0',
|
|
48
|
+
runs: [
|
|
49
|
+
{
|
|
50
|
+
tool: {
|
|
51
|
+
driver: {
|
|
52
|
+
name: 'OAuthLint',
|
|
53
|
+
version,
|
|
54
|
+
informationUri: 'https://oauthlint.dev',
|
|
55
|
+
rules: [...ruleIndex.values()],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
results: result.findings.map((f) => buildResult(f)),
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function buildRule(f) {
|
|
64
|
+
const tags = ['security', 'oauthlint'];
|
|
65
|
+
if (f.cwe)
|
|
66
|
+
tags.push(`external/cwe/${f.cwe.toLowerCase()}`);
|
|
67
|
+
if (f.llmPrevalence)
|
|
68
|
+
tags.push(`llm-prevalence/${f.llmPrevalence.toLowerCase()}`);
|
|
69
|
+
return {
|
|
70
|
+
id: f.ruleId,
|
|
71
|
+
name: f.ruleId,
|
|
72
|
+
shortDescription: { text: f.message.split('\n')[0]?.trim() ?? f.ruleId },
|
|
73
|
+
fullDescription: { text: f.message.trim() },
|
|
74
|
+
helpUri: f.docUrl,
|
|
75
|
+
defaultConfiguration: { level: SARIF_LEVEL[f.severity] },
|
|
76
|
+
properties: {
|
|
77
|
+
tags,
|
|
78
|
+
precision: 'medium',
|
|
79
|
+
security_severity: SECURITY_SEVERITY[f.severity],
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function buildResult(f) {
|
|
84
|
+
return {
|
|
85
|
+
ruleId: f.ruleId,
|
|
86
|
+
level: SARIF_LEVEL[f.severity],
|
|
87
|
+
message: { text: f.message.split('\n')[0]?.trim() ?? f.ruleId },
|
|
88
|
+
locations: [
|
|
89
|
+
{
|
|
90
|
+
physicalLocation: {
|
|
91
|
+
artifactLocation: { uri: relativise(f.filePath) },
|
|
92
|
+
region: { startLine: f.startLine, endLine: f.endLine },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
properties: {
|
|
97
|
+
oauthlintRuleId: f.oauthlintRuleId,
|
|
98
|
+
severity: f.severity,
|
|
99
|
+
llmPrevalence: f.llmPrevalence,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function relativise(filePath) {
|
|
104
|
+
// GitHub Code Scanning wants paths relative to the repo root. Strip
|
|
105
|
+
// the cwd if present; otherwise leave the path alone (caller responsibility).
|
|
106
|
+
const cwd = process.cwd();
|
|
107
|
+
if (filePath.startsWith(`${cwd}/`)) {
|
|
108
|
+
return filePath.slice(cwd.length + 1);
|
|
109
|
+
}
|
|
110
|
+
if (filePath.startsWith(cwd)) {
|
|
111
|
+
return filePath.slice(cwd.length).replace(/^\/+/, '');
|
|
112
|
+
}
|
|
113
|
+
return filePath;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../src/core/sarif.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type SeverityName } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Lower index = less severe. Used for "fail if any finding ≥ threshold".
|
|
4
|
+
*/
|
|
5
|
+
export declare function severityRank(s: SeverityName): number;
|
|
6
|
+
/**
|
|
7
|
+
* Return true if `actual` meets or exceeds the `threshold` severity.
|
|
8
|
+
*/
|
|
9
|
+
export declare function meetsThreshold(actual: SeverityName, threshold: SeverityName): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Map our 5-level scale to a process exit code:
|
|
12
|
+
* - 0 → no finding worth blocking on
|
|
13
|
+
* - 1 → at least one HIGH
|
|
14
|
+
* - 2 → at least one CRITICAL
|
|
15
|
+
*/
|
|
16
|
+
export declare function exitCodeFor(highest: SeverityName | null): number;
|
|
17
|
+
/**
|
|
18
|
+
* Find the highest severity in a list of findings.
|
|
19
|
+
*/
|
|
20
|
+
export declare function highestSeverity(findings: {
|
|
21
|
+
severity: SeverityName;
|
|
22
|
+
}[]): SeverityName | null;
|
|
23
|
+
//# sourceMappingURL=severity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"severity.d.ts","sourceRoot":"","sources":["../../src/core/severity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAE5D;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAGpD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,GAAG,OAAO,CAErF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,MAAM,CAKhE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE;IAAE,QAAQ,EAAE,YAAY,CAAA;CAAE,EAAE,GAAG,YAAY,GAAG,IAAI,CAQ3F"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SEVERITIES } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Lower index = less severe. Used for "fail if any finding ≥ threshold".
|
|
4
|
+
*/
|
|
5
|
+
export function severityRank(s) {
|
|
6
|
+
const idx = SEVERITIES.indexOf(s);
|
|
7
|
+
return idx === -1 ? 0 : idx;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Return true if `actual` meets or exceeds the `threshold` severity.
|
|
11
|
+
*/
|
|
12
|
+
export function meetsThreshold(actual, threshold) {
|
|
13
|
+
return severityRank(actual) >= severityRank(threshold);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Map our 5-level scale to a process exit code:
|
|
17
|
+
* - 0 → no finding worth blocking on
|
|
18
|
+
* - 1 → at least one HIGH
|
|
19
|
+
* - 2 → at least one CRITICAL
|
|
20
|
+
*/
|
|
21
|
+
export function exitCodeFor(highest) {
|
|
22
|
+
if (highest === null)
|
|
23
|
+
return 0;
|
|
24
|
+
if (severityRank(highest) >= severityRank('CRITICAL'))
|
|
25
|
+
return 2;
|
|
26
|
+
if (severityRank(highest) >= severityRank('HIGH'))
|
|
27
|
+
return 1;
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Find the highest severity in a list of findings.
|
|
32
|
+
*/
|
|
33
|
+
export function highestSeverity(findings) {
|
|
34
|
+
let highest = null;
|
|
35
|
+
for (const f of findings) {
|
|
36
|
+
if (!highest || severityRank(f.severity) > severityRank(highest)) {
|
|
37
|
+
highest = f.severity;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return highest;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=severity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"severity.js","sourceRoot":"","sources":["../../src/core/severity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAC;AAE5D;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,CAAe;IAC1C,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAAoB,EAAE,SAAuB;IAC1E,OAAO,YAAY,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,SAAS,CAAC,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAA4B;IACtD,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,YAAY,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,UAAU,CAAC;QAAE,OAAO,CAAC,CAAC;IAChE,IAAI,YAAY,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC;QAAE,OAAO,CAAC,CAAC;IAC5D,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAsC;IACpE,IAAI,OAAO,GAAwB,IAAI,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,IAAI,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;YACjE,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC;QACvB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Finding } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Inline suppression directives recognised by OAuthLint.
|
|
4
|
+
*
|
|
5
|
+
* `oauthlint-disable-next-line <rule-id> [-- <reason>]`
|
|
6
|
+
* Suppress findings on the immediately following line. The rule id is
|
|
7
|
+
* required (we don't support blanket "disable everything" because it
|
|
8
|
+
* silently hides future findings — bad smell). The reason is optional
|
|
9
|
+
* but encouraged.
|
|
10
|
+
*
|
|
11
|
+
* `oauthlint-disable-line <rule-id> [-- <reason>]`
|
|
12
|
+
* Same, but applies to the current line (the line the comment is on).
|
|
13
|
+
*
|
|
14
|
+
* `oauthlint-disable-file <rule-id> [-- <reason>]`
|
|
15
|
+
* Disable the rule for the entire file. Goes anywhere in the file.
|
|
16
|
+
*
|
|
17
|
+
* Glob `*` is accepted as a rule id placeholder for the wildcard case,
|
|
18
|
+
* but ONLY for `oauthlint-disable-next-line` / `disable-line` (not
|
|
19
|
+
* disable-file) so that wholesale silencing requires an explicit
|
|
20
|
+
* line-by-line decision.
|
|
21
|
+
*/
|
|
22
|
+
interface SuppressionMaps {
|
|
23
|
+
/** rule id (or '*') → set of zero-based line numbers to suppress */
|
|
24
|
+
byLine: Map<string, Set<number>>;
|
|
25
|
+
/** rule ids (or '*') suppressed for the whole file */
|
|
26
|
+
fileWide: Set<string>;
|
|
27
|
+
}
|
|
28
|
+
export declare function loadSuppressionMap(filePath: string): Promise<SuppressionMaps>;
|
|
29
|
+
export declare function parseSuppressionsFromSource(source: string): SuppressionMaps;
|
|
30
|
+
export declare function isSuppressed(finding: Finding, maps: SuppressionMaps): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Apply suppression to a list of findings. Caches the parsed directive
|
|
33
|
+
* map per-file so we only read each file once.
|
|
34
|
+
*/
|
|
35
|
+
export declare function applySuppressions(findings: Finding[]): Promise<{
|
|
36
|
+
kept: Finding[];
|
|
37
|
+
suppressed: Finding[];
|
|
38
|
+
}>;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=suppress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suppress.d.ts","sourceRoot":"","sources":["../../src/core/suppress.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,UAAU,eAAe;IACvB,oEAAoE;IACpE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACjC,sDAAsD;IACtD,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAID,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAQnF;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAgC3E;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAY7E;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACpE,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,UAAU,EAAE,OAAO,EAAE,CAAC;CACvB,CAAC,CAkBD"}
|