@obfuscan/core 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 +201 -0
- package/README.md +224 -0
- package/dist/allowlist.d.ts +25 -0
- package/dist/allowlist.d.ts.map +1 -0
- package/dist/allowlist.js +138 -0
- package/dist/allowlist.js.map +1 -0
- package/dist/detectors/bidi-control.d.ts +14 -0
- package/dist/detectors/bidi-control.d.ts.map +1 -0
- package/dist/detectors/bidi-control.js +67 -0
- package/dist/detectors/bidi-control.js.map +1 -0
- package/dist/detectors/cargo-build-rs-network.d.ts +12 -0
- package/dist/detectors/cargo-build-rs-network.d.ts.map +1 -0
- package/dist/detectors/cargo-build-rs-network.js +54 -0
- package/dist/detectors/cargo-build-rs-network.js.map +1 -0
- package/dist/detectors/decode-then-exec.d.ts +20 -0
- package/dist/detectors/decode-then-exec.d.ts.map +1 -0
- package/dist/detectors/decode-then-exec.js +189 -0
- package/dist/detectors/decode-then-exec.js.map +1 -0
- package/dist/detectors/deserializer-untrusted.d.ts +15 -0
- package/dist/detectors/deserializer-untrusted.d.ts.map +1 -0
- package/dist/detectors/deserializer-untrusted.js +99 -0
- package/dist/detectors/deserializer-untrusted.js.map +1 -0
- package/dist/detectors/dockerfile-curl-pipe-shell.d.ts +10 -0
- package/dist/detectors/dockerfile-curl-pipe-shell.d.ts.map +1 -0
- package/dist/detectors/dockerfile-curl-pipe-shell.js +42 -0
- package/dist/detectors/dockerfile-curl-pipe-shell.js.map +1 -0
- package/dist/detectors/dynamic-exec-non-literal.d.ts +17 -0
- package/dist/detectors/dynamic-exec-non-literal.d.ts.map +1 -0
- package/dist/detectors/dynamic-exec-non-literal.js +104 -0
- package/dist/detectors/dynamic-exec-non-literal.js.map +1 -0
- package/dist/detectors/encoded-array-fingerprint.d.ts +11 -0
- package/dist/detectors/encoded-array-fingerprint.d.ts.map +1 -0
- package/dist/detectors/encoded-array-fingerprint.js +60 -0
- package/dist/detectors/encoded-array-fingerprint.js.map +1 -0
- package/dist/detectors/gha-curl-pipe-shell.d.ts +11 -0
- package/dist/detectors/gha-curl-pipe-shell.d.ts.map +1 -0
- package/dist/detectors/gha-curl-pipe-shell.js +42 -0
- package/dist/detectors/gha-curl-pipe-shell.js.map +1 -0
- package/dist/detectors/high-entropy-literal.d.ts +19 -0
- package/dist/detectors/high-entropy-literal.d.ts.map +1 -0
- package/dist/detectors/high-entropy-literal.js +90 -0
- package/dist/detectors/high-entropy-literal.js.map +1 -0
- package/dist/detectors/homoglyph-identifier.d.ts +16 -0
- package/dist/detectors/homoglyph-identifier.d.ts.map +1 -0
- package/dist/detectors/homoglyph-identifier.js +76 -0
- package/dist/detectors/homoglyph-identifier.js.map +1 -0
- package/dist/detectors/index.d.ts +31 -0
- package/dist/detectors/index.d.ts.map +1 -0
- package/dist/detectors/index.js +60 -0
- package/dist/detectors/index.js.map +1 -0
- package/dist/detectors/library-load-non-literal.d.ts +10 -0
- package/dist/detectors/library-load-non-literal.d.ts.map +1 -0
- package/dist/detectors/library-load-non-literal.js +72 -0
- package/dist/detectors/library-load-non-literal.js.map +1 -0
- package/dist/detectors/long-line.d.ts +12 -0
- package/dist/detectors/long-line.d.ts.map +1 -0
- package/dist/detectors/long-line.js +53 -0
- package/dist/detectors/long-line.js.map +1 -0
- package/dist/detectors/manifest-install-script.d.ts +54 -0
- package/dist/detectors/manifest-install-script.d.ts.map +1 -0
- package/dist/detectors/manifest-install-script.js +272 -0
- package/dist/detectors/manifest-install-script.js.map +1 -0
- package/dist/detectors/network-then-exec.d.ts +17 -0
- package/dist/detectors/network-then-exec.d.ts.map +1 -0
- package/dist/detectors/network-then-exec.js +140 -0
- package/dist/detectors/network-then-exec.js.map +1 -0
- package/dist/detectors/perl-makefile-side-effect.d.ts +17 -0
- package/dist/detectors/perl-makefile-side-effect.d.ts.map +1 -0
- package/dist/detectors/perl-makefile-side-effect.js +72 -0
- package/dist/detectors/perl-makefile-side-effect.js.map +1 -0
- package/dist/detectors/python-setup-side-effect.d.ts +10 -0
- package/dist/detectors/python-setup-side-effect.d.ts.map +1 -0
- package/dist/detectors/python-setup-side-effect.js +87 -0
- package/dist/detectors/python-setup-side-effect.js.map +1 -0
- package/dist/detectors/shell-untrusted-input.d.ts +10 -0
- package/dist/detectors/shell-untrusted-input.d.ts.map +1 -0
- package/dist/detectors/shell-untrusted-input.js +76 -0
- package/dist/detectors/shell-untrusted-input.js.map +1 -0
- package/dist/detectors/string-array-decoder.d.ts +15 -0
- package/dist/detectors/string-array-decoder.d.ts.map +1 -0
- package/dist/detectors/string-array-decoder.js +70 -0
- package/dist/detectors/string-array-decoder.js.map +1 -0
- package/dist/detectors/suspicious-io-cluster.d.ts +11 -0
- package/dist/detectors/suspicious-io-cluster.d.ts.map +1 -0
- package/dist/detectors/suspicious-io-cluster.js +86 -0
- package/dist/detectors/suspicious-io-cluster.js.map +1 -0
- package/dist/diff.d.ts +23 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +144 -0
- package/dist/diff.js.map +1 -0
- package/dist/directives.d.ts +33 -0
- package/dist/directives.d.ts.map +1 -0
- package/dist/directives.js +60 -0
- package/dist/directives.js.map +1 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +16 -0
- package/dist/errors.js.map +1 -0
- package/dist/grammar/query.d.ts +44 -0
- package/dist/grammar/query.d.ts.map +1 -0
- package/dist/grammar/query.js +24 -0
- package/dist/grammar/query.js.map +1 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +106 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/patterns.d.ts +48 -0
- package/dist/internal/patterns.d.ts.map +1 -0
- package/dist/internal/patterns.js +95 -0
- package/dist/internal/patterns.js.map +1 -0
- package/dist/internal/text.d.ts +14 -0
- package/dist/internal/text.d.ts.map +1 -0
- package/dist/internal/text.js +20 -0
- package/dist/internal/text.js.map +1 -0
- package/dist/rules.d.ts +25 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +195 -0
- package/dist/rules.js.map +1 -0
- package/dist/scan.d.ts +26 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +287 -0
- package/dist/scan.js.map +1 -0
- package/dist/types.d.ts +215 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +50 -0
- package/dist/version.js.map +1 -0
- package/package.json +74 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @obfuscan/core — public entry point.
|
|
3
|
+
*
|
|
4
|
+
* Stable API surface. Anything not re-exported here is internal and may
|
|
5
|
+
* change without a major version bump.
|
|
6
|
+
*
|
|
7
|
+
* ## Quick start
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { scan } from "@obfuscan/core";
|
|
11
|
+
* import * as fs from "node:fs/promises";
|
|
12
|
+
*
|
|
13
|
+
* const result = await scan(
|
|
14
|
+
* { diff: await fs.readFile("pr.diff", "utf8") },
|
|
15
|
+
* { fileResolver: (p) => fs.readFile(p, "utf8") },
|
|
16
|
+
* );
|
|
17
|
+
*
|
|
18
|
+
* for (const f of result.findings) {
|
|
19
|
+
* console.log(`${f.severity.toUpperCase()} ${f.file}:${f.line} ${f.reason}`);
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* ## Custom rules
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { scan, loadRuleSet } from "@obfuscan/core";
|
|
27
|
+
*
|
|
28
|
+
* const rules = await loadRuleSet({
|
|
29
|
+
* languageDir: "./my-rules/languages",
|
|
30
|
+
* queryDir: "./my-rules/queries",
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* const result = await scan({ dir: "./src" }, { fileResolver, rules });
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* ## Custom detectors
|
|
37
|
+
*
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { scan, defaultDetectors, Detector } from "@obfuscan/core";
|
|
40
|
+
*
|
|
41
|
+
* const myDetector: Detector = {
|
|
42
|
+
* id: "my-org.no-fetch-in-tests",
|
|
43
|
+
* applies: (ctx) => /\.test\./.test(ctx.path),
|
|
44
|
+
* run: (ctx) => /* ... *\/ [],
|
|
45
|
+
* };
|
|
46
|
+
*
|
|
47
|
+
* const result = await scan(input, {
|
|
48
|
+
* fileResolver,
|
|
49
|
+
* detectors: [...defaultDetectors(), myDetector],
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
// ─── Primary entry point ───────────────────────────────────────────────────
|
|
54
|
+
export { scan } from "./scan.js";
|
|
55
|
+
// ─── Rule loading ──────────────────────────────────────────────────────────
|
|
56
|
+
/**
|
|
57
|
+
* Load a custom rule set from disk. Use this to point obfuscan at a fork of
|
|
58
|
+
* `@obfuscan/rules` or at an internal extension. If you only need the
|
|
59
|
+
* defaults, omit `rules` from `ScanOptions` and the built-in pack is used.
|
|
60
|
+
*/
|
|
61
|
+
export { loadRuleSet, defaultRuleSet } from "./rules.js";
|
|
62
|
+
// ─── Detectors ─────────────────────────────────────────────────────────────
|
|
63
|
+
/**
|
|
64
|
+
* The shipped detector set. Use this as a base when you want to add or
|
|
65
|
+
* filter detectors:
|
|
66
|
+
*
|
|
67
|
+
* ```ts
|
|
68
|
+
* const dets = defaultDetectors().filter(d => d.id !== "obf.high-entropy-literal");
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export { defaultDetectors } from "./detectors/index.js";
|
|
72
|
+
// ─── Allowlist helpers ─────────────────────────────────────────────────────
|
|
73
|
+
/**
|
|
74
|
+
* Read/write `.obfuscan/allowlist.json`. The CLI uses these directly.
|
|
75
|
+
* Programmatic users typically pass an inline `allowlist` to `scan()`
|
|
76
|
+
* instead of touching disk.
|
|
77
|
+
*/
|
|
78
|
+
export { loadAllowlist, saveAllowlist, hashSnippet, matchesAllowlist, } from "./allowlist.js";
|
|
79
|
+
// ─── Diff utilities ────────────────────────────────────────────────────────
|
|
80
|
+
/**
|
|
81
|
+
* Parse a unified diff into the shape `scan()` consumes internally.
|
|
82
|
+
* Exposed for hosts (e.g. a Git client) that already have a structured diff
|
|
83
|
+
* representation and want to skip re-serializing.
|
|
84
|
+
*/
|
|
85
|
+
export { parseDiffToFiles } from "./diff.js";
|
|
86
|
+
// ─── In-source suppression directives ──────────────────────────────────────
|
|
87
|
+
/**
|
|
88
|
+
* Parses `// obfuscan-disable-next-line <ruleId>` and similar comment
|
|
89
|
+
* directives. Returns the set of (line, ruleId) pairs to suppress.
|
|
90
|
+
* Used internally by the aggregator; exposed for tooling that wants to
|
|
91
|
+
* surface them in a UI.
|
|
92
|
+
*/
|
|
93
|
+
export { extractDisableDirectives } from "./directives.js";
|
|
94
|
+
// ─── Versions ──────────────────────────────────────────────────────────────
|
|
95
|
+
/** SemVer of the engine. Replaced at build time via `__ENGINE_VERSION__`. */
|
|
96
|
+
export { ENGINE_VERSION } from "./version.js";
|
|
97
|
+
// ─── Errors ────────────────────────────────────────────────────────────────
|
|
98
|
+
/**
|
|
99
|
+
* `InvalidScanInputError` — thrown when `ScanInput` is missing all of
|
|
100
|
+
* `diff`, `paths`, `dir`, or specifies more than one.
|
|
101
|
+
*
|
|
102
|
+
* `InvalidRuleSetError` — thrown when a custom rule set fails validation.
|
|
103
|
+
* Includes a `details` array describing each problem.
|
|
104
|
+
*/
|
|
105
|
+
export { InvalidScanInputError, InvalidRuleSetError } from "./errors.js";
|
|
106
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,8EAA8E;AAE9E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA4BjC,8EAA8E;AAE9E;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzD,8EAA8E;AAE9E;;;;;;;GAOG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,8EAA8E;AAE9E;;;;GAIG;AACH,OAAO,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,8EAA8E;AAE9E;;;;GAIG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAG7C,8EAA8E;AAE9E;;;;;GAKG;AACH,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAG3D,8EAA8E;AAE9E,6EAA6E;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,8EAA8E;AAE9E;;;;;;GAMG;AACH,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern-building helpers shared by detectors.
|
|
3
|
+
*
|
|
4
|
+
* The bundled detectors are intentionally regex-driven rather than parse-tree
|
|
5
|
+
* driven — that keeps the engine dependency-free and runnable in any Node
|
|
6
|
+
* environment. The regexes are built dynamically from `LanguageConfig` so the
|
|
7
|
+
* same detector covers every supported language.
|
|
8
|
+
*
|
|
9
|
+
* Tradeoff: regex matching is approximate (no full scope analysis). False
|
|
10
|
+
* positives are mitigated by:
|
|
11
|
+
* - Severity ceilings + allowlist
|
|
12
|
+
* - The shape of the patterns (e.g. requiring an *immediate* call wrap)
|
|
13
|
+
* - The aggregator's `disabledDetectors` and in-source directives
|
|
14
|
+
*
|
|
15
|
+
* For airtight semantic analysis, hosts can override `defaultDetectors()`
|
|
16
|
+
* with tree-sitter-backed implementations.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Escape a string for use as a literal inside a regex.
|
|
20
|
+
* Handles the dotted/qualified names that appear in language configs
|
|
21
|
+
* (e.g. `base64.b64decode`, `Buffer.from`, `[Convert]::FromBase64String`).
|
|
22
|
+
*/
|
|
23
|
+
export declare function escapeRegex(s: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Build a regex source matching any of the configured names as a function
|
|
26
|
+
* call. Accepts qualified names — for `base64.b64decode` the regex matches
|
|
27
|
+
* either the bare `b64decode(...)` or fully-qualified `base64.b64decode(...)`.
|
|
28
|
+
*
|
|
29
|
+
* The boundary on the left tolerates common JS/TS/Py prefixes (`.`, `(`,
|
|
30
|
+
* `=`, `,`, whitespace, start-of-line) so we don't miss expression contexts.
|
|
31
|
+
*
|
|
32
|
+
* Returns an inner alternation suitable for inclusion in a larger pattern.
|
|
33
|
+
*/
|
|
34
|
+
export declare function namedCallAlternation(names: readonly string[]): string;
|
|
35
|
+
/**
|
|
36
|
+
* 1-based line number for a 0-based character offset.
|
|
37
|
+
* Used by every detector to report source positions.
|
|
38
|
+
*/
|
|
39
|
+
export declare function lineAtOffset(source: string, offset: number): number;
|
|
40
|
+
/**
|
|
41
|
+
* 1-based end line of a span starting at `start` and consuming `length` chars.
|
|
42
|
+
*/
|
|
43
|
+
export declare function lineAtEnd(source: string, start: number, length: number): number;
|
|
44
|
+
/** Cap on how many findings any single detector emits per file. */
|
|
45
|
+
export declare const MAX_FINDINGS_PER_DETECTOR = 50;
|
|
46
|
+
/** Cap on file size before detectors short-circuit. */
|
|
47
|
+
export declare const MAX_SOURCE_BYTES = 2000000;
|
|
48
|
+
//# sourceMappingURL=patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/internal/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAkCrE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAOnE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED,mEAAmE;AACnE,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,uDAAuD;AACvD,eAAO,MAAM,gBAAgB,UAAY,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern-building helpers shared by detectors.
|
|
3
|
+
*
|
|
4
|
+
* The bundled detectors are intentionally regex-driven rather than parse-tree
|
|
5
|
+
* driven — that keeps the engine dependency-free and runnable in any Node
|
|
6
|
+
* environment. The regexes are built dynamically from `LanguageConfig` so the
|
|
7
|
+
* same detector covers every supported language.
|
|
8
|
+
*
|
|
9
|
+
* Tradeoff: regex matching is approximate (no full scope analysis). False
|
|
10
|
+
* positives are mitigated by:
|
|
11
|
+
* - Severity ceilings + allowlist
|
|
12
|
+
* - The shape of the patterns (e.g. requiring an *immediate* call wrap)
|
|
13
|
+
* - The aggregator's `disabledDetectors` and in-source directives
|
|
14
|
+
*
|
|
15
|
+
* For airtight semantic analysis, hosts can override `defaultDetectors()`
|
|
16
|
+
* with tree-sitter-backed implementations.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Escape a string for use as a literal inside a regex.
|
|
20
|
+
* Handles the dotted/qualified names that appear in language configs
|
|
21
|
+
* (e.g. `base64.b64decode`, `Buffer.from`, `[Convert]::FromBase64String`).
|
|
22
|
+
*/
|
|
23
|
+
export function escapeRegex(s) {
|
|
24
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build a regex source matching any of the configured names as a function
|
|
28
|
+
* call. Accepts qualified names — for `base64.b64decode` the regex matches
|
|
29
|
+
* either the bare `b64decode(...)` or fully-qualified `base64.b64decode(...)`.
|
|
30
|
+
*
|
|
31
|
+
* The boundary on the left tolerates common JS/TS/Py prefixes (`.`, `(`,
|
|
32
|
+
* `=`, `,`, whitespace, start-of-line) so we don't miss expression contexts.
|
|
33
|
+
*
|
|
34
|
+
* Returns an inner alternation suitable for inclusion in a larger pattern.
|
|
35
|
+
*/
|
|
36
|
+
export function namedCallAlternation(names) {
|
|
37
|
+
const alts = [];
|
|
38
|
+
for (const raw of names) {
|
|
39
|
+
if (!raw)
|
|
40
|
+
continue;
|
|
41
|
+
// Special-case PowerShell's `[Type]::Member` form
|
|
42
|
+
if (/^\[.+\]::/.test(raw)) {
|
|
43
|
+
alts.push(escapeRegex(raw));
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
// Special-case Bash pipelines like `base64 -d` or `base64 --decode`
|
|
47
|
+
if (/\s/.test(raw)) {
|
|
48
|
+
alts.push(escapeRegex(raw));
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
// Split on either `.` (Python/JS) or `::` (Perl/Ruby/Rust/C++) to find
|
|
52
|
+
// the bare tail. We accept both because configs mix conventions across
|
|
53
|
+
// languages (e.g. `MIME::Base64::decode_base64`, `base64.b64decode`).
|
|
54
|
+
const parts = raw.split(/\.|::/);
|
|
55
|
+
const tail = parts[parts.length - 1] ?? raw;
|
|
56
|
+
alts.push(escapeRegex(raw));
|
|
57
|
+
if (parts.length > 1 && tail !== raw && tail.length > 0) {
|
|
58
|
+
alts.push(escapeRegex(tail));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Deduplicate while preserving order
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
const unique = [];
|
|
64
|
+
for (const a of alts) {
|
|
65
|
+
if (!seen.has(a)) {
|
|
66
|
+
seen.add(a);
|
|
67
|
+
unique.push(a);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return unique.join("|");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 1-based line number for a 0-based character offset.
|
|
74
|
+
* Used by every detector to report source positions.
|
|
75
|
+
*/
|
|
76
|
+
export function lineAtOffset(source, offset) {
|
|
77
|
+
let line = 1;
|
|
78
|
+
const cap = Math.min(offset, source.length);
|
|
79
|
+
for (let i = 0; i < cap; i++) {
|
|
80
|
+
if (source.charCodeAt(i) === 10 /* \n */)
|
|
81
|
+
line++;
|
|
82
|
+
}
|
|
83
|
+
return line;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 1-based end line of a span starting at `start` and consuming `length` chars.
|
|
87
|
+
*/
|
|
88
|
+
export function lineAtEnd(source, start, length) {
|
|
89
|
+
return lineAtOffset(source, start + length);
|
|
90
|
+
}
|
|
91
|
+
/** Cap on how many findings any single detector emits per file. */
|
|
92
|
+
export const MAX_FINDINGS_PER_DETECTOR = 50;
|
|
93
|
+
/** Cap on file size before detectors short-circuit. */
|
|
94
|
+
export const MAX_SOURCE_BYTES = 2_000_000;
|
|
95
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/internal/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAwB;IAC3D,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,kDAAkD;QAClD,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5B,SAAS;QACX,CAAC;QACD,oEAAoE;QACpE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5B,SAAS;QACX,CAAC;QACD,uEAAuE;QACvE,uEAAuE;QACvE,sEAAsE;QACtE,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC;QAC5C,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxD,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,qCAAqC;IACrC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,MAAc;IACzD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,QAAQ;YAAE,IAAI,EAAE,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,KAAa,EAAE,MAAc;IACrE,OAAO,YAAY,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,mEAAmE;AACnE,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAE5C,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal text helpers shared across detectors.
|
|
3
|
+
*
|
|
4
|
+
* Anything in `src/internal/` is NOT part of the public API and may change
|
|
5
|
+
* without a major version bump. Detectors are allowed to import from here.
|
|
6
|
+
* External consumers must not.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Truncate a snippet to `Finding.snippet`'s documented 200-char ceiling.
|
|
10
|
+
* The engine also enforces this defensively, but doing it in the detector
|
|
11
|
+
* keeps `Finding.evidence` payloads consistent with what the user sees.
|
|
12
|
+
*/
|
|
13
|
+
export declare function truncateSnippet(s: string): string;
|
|
14
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/internal/text.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal text helpers shared across detectors.
|
|
3
|
+
*
|
|
4
|
+
* Anything in `src/internal/` is NOT part of the public API and may change
|
|
5
|
+
* without a major version bump. Detectors are allowed to import from here.
|
|
6
|
+
* External consumers must not.
|
|
7
|
+
*/
|
|
8
|
+
const MAX_SNIPPET_LEN = 200;
|
|
9
|
+
const ELLIPSIS = "...";
|
|
10
|
+
/**
|
|
11
|
+
* Truncate a snippet to `Finding.snippet`'s documented 200-char ceiling.
|
|
12
|
+
* The engine also enforces this defensively, but doing it in the detector
|
|
13
|
+
* keeps `Finding.evidence` payloads consistent with what the user sees.
|
|
14
|
+
*/
|
|
15
|
+
export function truncateSnippet(s) {
|
|
16
|
+
if (s.length <= MAX_SNIPPET_LEN)
|
|
17
|
+
return s;
|
|
18
|
+
return s.slice(0, MAX_SNIPPET_LEN - ELLIPSIS.length) + ELLIPSIS;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/internal/text.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,eAAe,GAAG,GAAG,CAAC;AAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC;AAEvB;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,CAAS;IACvC,IAAI,CAAC,CAAC,MAAM,IAAI,eAAe;QAAE,OAAO,CAAC,CAAC;IAC1C,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;AAClE,CAAC"}
|
package/dist/rules.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule pack loader.
|
|
3
|
+
*
|
|
4
|
+
* Loads `LanguageConfig` JSON files from a directory (or from the bundled
|
|
5
|
+
* @obfuscan/rules package) and exposes a `RuleSet` interface used by the
|
|
6
|
+
* scan loop.
|
|
7
|
+
*
|
|
8
|
+
* No tree-sitter grammars are loaded eagerly — `loadGrammar()` is a stub that
|
|
9
|
+
* returns a sentinel handle. The bundled detectors operate on raw source via
|
|
10
|
+
* regex/AST-lite techniques and don't require a live parser. Hosts that want
|
|
11
|
+
* full tree-sitter parsing can implement their own `RuleSet` and pass it via
|
|
12
|
+
* `ScanOptions.rules`.
|
|
13
|
+
*/
|
|
14
|
+
import type { RuleSet } from "./types.js";
|
|
15
|
+
interface LoadOptions {
|
|
16
|
+
/** Directory containing per-language *.json configs. */
|
|
17
|
+
languageDir: string;
|
|
18
|
+
/** Optional directory of .scm queries. Currently informational; reserved for future use. */
|
|
19
|
+
queryDir?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Build a RuleSet by scanning a directory of language configs. */
|
|
22
|
+
export declare function loadRuleSet(opts: LoadOptions): Promise<RuleSet>;
|
|
23
|
+
export declare function defaultRuleSet(): Promise<RuleSet>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAiC,OAAO,EAAE,MAAM,YAAY,CAAC;AAGzE,UAAU,WAAW;IACnB,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mEAAmE;AACnE,wBAAsB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAsFrE;AA0CD,wBAAsB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAkBvD"}
|
package/dist/rules.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule pack loader.
|
|
3
|
+
*
|
|
4
|
+
* Loads `LanguageConfig` JSON files from a directory (or from the bundled
|
|
5
|
+
* @obfuscan/rules package) and exposes a `RuleSet` interface used by the
|
|
6
|
+
* scan loop.
|
|
7
|
+
*
|
|
8
|
+
* No tree-sitter grammars are loaded eagerly — `loadGrammar()` is a stub that
|
|
9
|
+
* returns a sentinel handle. The bundled detectors operate on raw source via
|
|
10
|
+
* regex/AST-lite techniques and don't require a live parser. Hosts that want
|
|
11
|
+
* full tree-sitter parsing can implement their own `RuleSet` and pass it via
|
|
12
|
+
* `ScanOptions.rules`.
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from "node:fs/promises";
|
|
15
|
+
import * as path from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { InvalidRuleSetError } from "./errors.js";
|
|
18
|
+
/** Build a RuleSet by scanning a directory of language configs. */
|
|
19
|
+
export async function loadRuleSet(opts) {
|
|
20
|
+
const { languageDir } = opts;
|
|
21
|
+
const entries = await fs.readdir(languageDir).catch((e) => {
|
|
22
|
+
throw new InvalidRuleSetError(`failed to read language directory: ${languageDir}`, [{ file: languageDir, problem: String(e) }]);
|
|
23
|
+
});
|
|
24
|
+
const configs = new Map();
|
|
25
|
+
const aliasIndex = new Map();
|
|
26
|
+
const extIndex = new Map();
|
|
27
|
+
const filenameIndex = new Map();
|
|
28
|
+
const problems = [];
|
|
29
|
+
for (const file of entries) {
|
|
30
|
+
if (!file.endsWith(".json"))
|
|
31
|
+
continue;
|
|
32
|
+
if (file.startsWith("_"))
|
|
33
|
+
continue; // skip _schema.json, _template.json
|
|
34
|
+
const full = path.join(languageDir, file);
|
|
35
|
+
let raw;
|
|
36
|
+
try {
|
|
37
|
+
raw = await fs.readFile(full, "utf8");
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
problems.push({ file, problem: `read failed: ${String(e)}` });
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(raw);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
problems.push({ file, problem: `invalid JSON: ${String(e)}` });
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const cfg = parsed;
|
|
52
|
+
if (typeof cfg.id !== "string") {
|
|
53
|
+
problems.push({ file, problem: "missing string field: id" });
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (!Array.isArray(cfg.extensions)) {
|
|
57
|
+
problems.push({ file, problem: "missing array field: extensions" });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (!Array.isArray(cfg.dynamic_exec_sinks)) {
|
|
61
|
+
problems.push({ file, problem: "missing array field: dynamic_exec_sinks" });
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!Array.isArray(cfg.decoders)) {
|
|
65
|
+
problems.push({ file, problem: "missing array field: decoders" });
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const config = cfg;
|
|
69
|
+
configs.set(config.id, config);
|
|
70
|
+
aliasIndex.set(config.id, config.id);
|
|
71
|
+
for (const alias of config.aliases ?? []) {
|
|
72
|
+
aliasIndex.set(alias, config.id);
|
|
73
|
+
}
|
|
74
|
+
for (const ext of config.extensions) {
|
|
75
|
+
extIndex.set(ext.toLowerCase(), config.id);
|
|
76
|
+
}
|
|
77
|
+
for (const fname of config.filenames ?? []) {
|
|
78
|
+
filenameIndex.set(fname, config.id);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (problems.length > 0 && configs.size === 0) {
|
|
82
|
+
throw new InvalidRuleSetError(`no valid language configs in ${languageDir}`, problems);
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
languages: () => Array.from(configs.keys()).sort(),
|
|
86
|
+
configFor: (id) => configs.get(aliasIndex.get(id) ?? id) ?? null,
|
|
87
|
+
detectLanguage: (p) => detectLanguage(p, extIndex, filenameIndex),
|
|
88
|
+
loadGrammar: async (id) => {
|
|
89
|
+
// Stub: detectors that depend on a live parser would override this via
|
|
90
|
+
// a host-supplied RuleSet. The bundled detectors don't need it.
|
|
91
|
+
return Object.freeze({ id, _internal: null });
|
|
92
|
+
},
|
|
93
|
+
version: () => "0.0.0-source", // placeholder; defaultRuleSet() supplies real CalVer
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function detectLanguage(filePath, extIndex, filenameIndex) {
|
|
97
|
+
const norm = filePath.replace(/\\/g, "/");
|
|
98
|
+
const base = norm.slice(norm.lastIndexOf("/") + 1);
|
|
99
|
+
// Filename match first (Dockerfile, build.rs, package.json, setup.py, …)
|
|
100
|
+
const exact = filenameIndex.get(base);
|
|
101
|
+
if (exact)
|
|
102
|
+
return exact;
|
|
103
|
+
// Common manifests by basename when no language config claims them
|
|
104
|
+
if (base === "package.json")
|
|
105
|
+
return "json";
|
|
106
|
+
if (base === "Dockerfile" || base.startsWith("Dockerfile."))
|
|
107
|
+
return "dockerfile";
|
|
108
|
+
if (norm.includes("/.github/workflows/") && (base.endsWith(".yml") || base.endsWith(".yaml"))) {
|
|
109
|
+
return "yaml";
|
|
110
|
+
}
|
|
111
|
+
// Extension match
|
|
112
|
+
const lastDot = base.lastIndexOf(".");
|
|
113
|
+
if (lastDot >= 0) {
|
|
114
|
+
const ext = base.slice(lastDot).toLowerCase();
|
|
115
|
+
const id = extIndex.get(ext);
|
|
116
|
+
if (id)
|
|
117
|
+
return id;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
// ─── Default rule set ──────────────────────────────────────────────────────
|
|
122
|
+
//
|
|
123
|
+
// The bundled @obfuscan/rules package is resolved via Node's module resolver.
|
|
124
|
+
// We try several candidate locations in order so this works whether
|
|
125
|
+
// @obfuscan/rules is installed via npm, linked, or bundled in a workspace.
|
|
126
|
+
const RULES_VERSION_FALLBACK = "2026.04.0";
|
|
127
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
128
|
+
let cachedDefault = null;
|
|
129
|
+
export async function defaultRuleSet() {
|
|
130
|
+
if (cachedDefault)
|
|
131
|
+
return cachedDefault;
|
|
132
|
+
const candidates = await locateBundledRules();
|
|
133
|
+
for (const dir of candidates) {
|
|
134
|
+
try {
|
|
135
|
+
const rs = await loadRuleSet({ languageDir: dir });
|
|
136
|
+
const version = await readPackageVersion(path.dirname(dir)) ?? RULES_VERSION_FALLBACK;
|
|
137
|
+
cachedDefault = wrapWithVersion(rs, version);
|
|
138
|
+
return cachedDefault;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// try next candidate
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Last resort: an empty rule set so the engine still runs.
|
|
145
|
+
cachedDefault = emptyRuleSet();
|
|
146
|
+
return cachedDefault;
|
|
147
|
+
}
|
|
148
|
+
async function locateBundledRules() {
|
|
149
|
+
const candidates = [];
|
|
150
|
+
// 1. Sibling workspace package: ../rules/languages
|
|
151
|
+
candidates.push(path.resolve(here, "..", "..", "rules", "languages"));
|
|
152
|
+
// 2. Installed via npm: node_modules/@obfuscan/rules/languages
|
|
153
|
+
try {
|
|
154
|
+
const req = (await import("node:module")).createRequire(import.meta.url);
|
|
155
|
+
const pkgPath = req.resolve("@obfuscan/rules/package.json");
|
|
156
|
+
candidates.push(path.join(path.dirname(pkgPath), "languages"));
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// not resolvable; skip
|
|
160
|
+
}
|
|
161
|
+
// 3. Env override
|
|
162
|
+
const envDir = process.env["OBFUSCAN_RULES_DIR"];
|
|
163
|
+
if (envDir)
|
|
164
|
+
candidates.unshift(envDir);
|
|
165
|
+
return candidates;
|
|
166
|
+
}
|
|
167
|
+
async function readPackageVersion(dir) {
|
|
168
|
+
try {
|
|
169
|
+
const raw = await fs.readFile(path.join(dir, "package.json"), "utf8");
|
|
170
|
+
const pkg = JSON.parse(raw);
|
|
171
|
+
return typeof pkg.version === "string" ? pkg.version : null;
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function wrapWithVersion(rs, version) {
|
|
178
|
+
return {
|
|
179
|
+
languages: () => rs.languages(),
|
|
180
|
+
configFor: id => rs.configFor(id),
|
|
181
|
+
detectLanguage: p => rs.detectLanguage(p),
|
|
182
|
+
loadGrammar: id => rs.loadGrammar(id),
|
|
183
|
+
version: () => version,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function emptyRuleSet() {
|
|
187
|
+
return {
|
|
188
|
+
languages: () => [],
|
|
189
|
+
configFor: () => null,
|
|
190
|
+
detectLanguage: () => null,
|
|
191
|
+
loadGrammar: async (id) => Object.freeze({ id, _internal: null }),
|
|
192
|
+
version: () => RULES_VERSION_FALLBACK,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AASlD,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAiB;IACjD,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAC7B,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;QACjE,MAAM,IAAI,mBAAmB,CAC3B,sCAAsC,WAAW,EAAE,EACnD,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAC5C,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAClD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEhD,MAAM,QAAQ,GAA6C,EAAE,CAAC;IAE9D,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS,CAAC,oCAAoC;QAExE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAC1C,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC9D,SAAS;QACX,CAAC;QACD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/D,SAAS;QACX,CAAC;QAED,MAAM,GAAG,GAAG,MAAiC,CAAC;QAC9C,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,CAAC;YAC7D,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACnC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC,CAAC;YACpE,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC3C,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,yCAAyC,EAAE,CAAC,CAAC;YAC5E,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC,CAAC;YAClE,SAAS;QACX,CAAC;QAED,MAAM,MAAM,GAAG,GAAqB,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC/B,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACrC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;YACzC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACpC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;YAC3C,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,mBAAmB,CAC3B,gCAAgC,WAAW,EAAE,EAC7C,QAAQ,CACT,CAAC;IACJ,CAAC;IAED,OAAO;QACL,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QAClD,SAAS,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI;QACxE,cAAc,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC;QACzE,WAAW,EAAE,KAAK,EAAE,EAAU,EAA0B,EAAE;YACxD,uEAAuE;YACvE,gEAAgE;YAChE,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,EAAE,qDAAqD;KACrF,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CACrB,QAAgB,EAChB,QAA6B,EAC7B,aAAkC;IAElC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAEnD,yEAAyE;IACzE,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IAExB,mEAAmE;IACnE,IAAI,IAAI,KAAK,cAAc;QAAE,OAAO,MAAM,CAAC;IAC3C,IAAI,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,YAAY,CAAC;IACjF,IAAI,IAAI,CAAC,QAAQ,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC9F,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,kBAAkB;IAClB,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9C,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE;YAAE,OAAO,EAAE,CAAC;IACpB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8EAA8E;AAC9E,EAAE;AACF,8EAA8E;AAC9E,oEAAoE;AACpE,2EAA2E;AAE3E,MAAM,sBAAsB,GAAG,WAAW,CAAC;AAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,IAAI,aAAa,GAAmB,IAAI,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IAExC,MAAM,UAAU,GAAG,MAAM,kBAAkB,EAAE,CAAC;IAC9C,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;YACnD,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,sBAAsB,CAAC;YACtF,aAAa,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YAC7C,OAAO,aAAa,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,aAAa,GAAG,YAAY,EAAE,CAAC;IAC/B,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,mDAAmD;IACnD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAEtE,+DAA+D;IAC/D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;QAC5D,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;IAED,kBAAkB;IAClB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACjD,IAAI,MAAM;QAAE,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAEvC,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,GAAW;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;QACtE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC;QACpD,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,EAAW,EAAE,OAAe;IACnD,OAAO;QACL,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE;QAC/B,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;QACjC,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;QACzC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;QACrC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO;KACvB,CAAC;AACJ,CAAC;AAED,SAAS,YAAY;IACnB,OAAO;QACL,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE;QACnB,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI;QACrB,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;QAC1B,WAAW,EAAE,KAAK,EAAE,EAAU,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACzE,OAAO,EAAE,GAAG,EAAE,CAAC,sBAAsB;KACtC,CAAC;AACJ,CAAC"}
|
package/dist/scan.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scan() — the orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline:
|
|
5
|
+
* 1. Validate ScanInput (exactly one of diff/paths/dir).
|
|
6
|
+
* 2. Resolve target file list:
|
|
7
|
+
* diff → parseDiffToFiles → addedRanges per file
|
|
8
|
+
* paths → use as-is, no addedRanges
|
|
9
|
+
* dir → host enumerates; we just respect the resolver's nulls
|
|
10
|
+
* 3. For each file (concurrency-limited):
|
|
11
|
+
* a. Resolve content via fileResolver (catch errors; log+skip)
|
|
12
|
+
* b. Build FileContext (path, source, languageId, config, addedRanges)
|
|
13
|
+
* c. For each enabled detector where applies(ctx) is true:
|
|
14
|
+
* - run() with per-file timeout + try/catch
|
|
15
|
+
* - collect findings
|
|
16
|
+
* d. Apply diff-range filter (drop findings outside addedRanges)
|
|
17
|
+
* e. Apply in-source disable directives
|
|
18
|
+
* f. Apply allowlist (paths + snippets)
|
|
19
|
+
* g. Apply minSeverity
|
|
20
|
+
* h. Truncate snippets to 200 chars defensively
|
|
21
|
+
* 4. Sort findings by (severity desc, score desc, file asc, line asc).
|
|
22
|
+
* 5. Return ScanResult.
|
|
23
|
+
*/
|
|
24
|
+
import type { ScanInput, ScanOptions, ScanResult } from "./types.js";
|
|
25
|
+
export declare function scan(input: ScanInput, options: ScanOptions): Promise<ScanResult>;
|
|
26
|
+
//# sourceMappingURL=scan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAIV,SAAS,EACT,WAAW,EAEX,UAAU,EAIX,MAAM,YAAY,CAAC;AAoBpB,wBAAsB,IAAI,CACxB,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CA4KrB"}
|