@shipsafe/cli 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/README.md +167 -0
- package/dist/bin/shipsafe.d.ts +3 -0
- package/dist/bin/shipsafe.d.ts.map +1 -0
- package/dist/bin/shipsafe.js +33 -0
- package/dist/bin/shipsafe.js.map +1 -0
- package/dist/src/autofix/pr-generator.d.ts +48 -0
- package/dist/src/autofix/pr-generator.d.ts.map +1 -0
- package/dist/src/autofix/pr-generator.js +359 -0
- package/dist/src/autofix/pr-generator.js.map +1 -0
- package/dist/src/autofix/scaffolding.d.ts +26 -0
- package/dist/src/autofix/scaffolding.d.ts.map +1 -0
- package/dist/src/autofix/scaffolding.js +249 -0
- package/dist/src/autofix/scaffolding.js.map +1 -0
- package/dist/src/autofix/secret-fixer.d.ts +27 -0
- package/dist/src/autofix/secret-fixer.d.ts.map +1 -0
- package/dist/src/autofix/secret-fixer.js +138 -0
- package/dist/src/autofix/secret-fixer.js.map +1 -0
- package/dist/src/claude-md/manager.d.ts +17 -0
- package/dist/src/claude-md/manager.d.ts.map +1 -0
- package/dist/src/claude-md/manager.js +143 -0
- package/dist/src/claude-md/manager.js.map +1 -0
- package/dist/src/cli/activate.d.ts +4 -0
- package/dist/src/cli/activate.d.ts.map +1 -0
- package/dist/src/cli/activate.js +53 -0
- package/dist/src/cli/activate.js.map +1 -0
- package/dist/src/cli/config.d.ts +21 -0
- package/dist/src/cli/config.d.ts.map +1 -0
- package/dist/src/cli/config.js +128 -0
- package/dist/src/cli/config.js.map +1 -0
- package/dist/src/cli/connect.d.ts +36 -0
- package/dist/src/cli/connect.d.ts.map +1 -0
- package/dist/src/cli/connect.js +107 -0
- package/dist/src/cli/connect.js.map +1 -0
- package/dist/src/cli/init.d.ts +12 -0
- package/dist/src/cli/init.d.ts.map +1 -0
- package/dist/src/cli/init.js +45 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/license-check.d.ts +7 -0
- package/dist/src/cli/license-check.d.ts.map +1 -0
- package/dist/src/cli/license-check.js +69 -0
- package/dist/src/cli/license-check.js.map +1 -0
- package/dist/src/cli/license-gate.d.ts +9 -0
- package/dist/src/cli/license-gate.d.ts.map +1 -0
- package/dist/src/cli/license-gate.js +25 -0
- package/dist/src/cli/license-gate.js.map +1 -0
- package/dist/src/cli/scan.d.ts +9 -0
- package/dist/src/cli/scan.d.ts.map +1 -0
- package/dist/src/cli/scan.js +75 -0
- package/dist/src/cli/scan.js.map +1 -0
- package/dist/src/cli/setup.d.ts +27 -0
- package/dist/src/cli/setup.d.ts.map +1 -0
- package/dist/src/cli/setup.js +134 -0
- package/dist/src/cli/setup.js.map +1 -0
- package/dist/src/cli/status.d.ts +4 -0
- package/dist/src/cli/status.d.ts.map +1 -0
- package/dist/src/cli/status.js +52 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/cli/upload-sourcemaps.d.ts +13 -0
- package/dist/src/cli/upload-sourcemaps.d.ts.map +1 -0
- package/dist/src/cli/upload-sourcemaps.js +157 -0
- package/dist/src/cli/upload-sourcemaps.js.map +1 -0
- package/dist/src/config/manager.d.ts +37 -0
- package/dist/src/config/manager.d.ts.map +1 -0
- package/dist/src/config/manager.js +131 -0
- package/dist/src/config/manager.js.map +1 -0
- package/dist/src/constants.d.ts +28 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +34 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/engines/graph/data-flow.d.ts +36 -0
- package/dist/src/engines/graph/data-flow.d.ts.map +1 -0
- package/dist/src/engines/graph/data-flow.js +189 -0
- package/dist/src/engines/graph/data-flow.js.map +1 -0
- package/dist/src/engines/graph/index.d.ts +20 -0
- package/dist/src/engines/graph/index.d.ts.map +1 -0
- package/dist/src/engines/graph/index.js +100 -0
- package/dist/src/engines/graph/index.js.map +1 -0
- package/dist/src/engines/graph/parser.d.ts +13 -0
- package/dist/src/engines/graph/parser.d.ts.map +1 -0
- package/dist/src/engines/graph/parser.js +620 -0
- package/dist/src/engines/graph/parser.js.map +1 -0
- package/dist/src/engines/graph/queries.d.ts +11 -0
- package/dist/src/engines/graph/queries.d.ts.map +1 -0
- package/dist/src/engines/graph/queries.js +196 -0
- package/dist/src/engines/graph/queries.js.map +1 -0
- package/dist/src/engines/graph/store.d.ts +35 -0
- package/dist/src/engines/graph/store.d.ts.map +1 -0
- package/dist/src/engines/graph/store.js +284 -0
- package/dist/src/engines/graph/store.js.map +1 -0
- package/dist/src/engines/pattern/gitleaks.d.ts +4 -0
- package/dist/src/engines/pattern/gitleaks.d.ts.map +1 -0
- package/dist/src/engines/pattern/gitleaks.js +78 -0
- package/dist/src/engines/pattern/gitleaks.js.map +1 -0
- package/dist/src/engines/pattern/index.d.ts +11 -0
- package/dist/src/engines/pattern/index.d.ts.map +1 -0
- package/dist/src/engines/pattern/index.js +111 -0
- package/dist/src/engines/pattern/index.js.map +1 -0
- package/dist/src/engines/pattern/semgrep.d.ts +4 -0
- package/dist/src/engines/pattern/semgrep.d.ts.map +1 -0
- package/dist/src/engines/pattern/semgrep.js +83 -0
- package/dist/src/engines/pattern/semgrep.js.map +1 -0
- package/dist/src/engines/pattern/trivy.d.ts +4 -0
- package/dist/src/engines/pattern/trivy.d.ts.map +1 -0
- package/dist/src/engines/pattern/trivy.js +90 -0
- package/dist/src/engines/pattern/trivy.js.map +1 -0
- package/dist/src/github/api.d.ts +19 -0
- package/dist/src/github/api.d.ts.map +1 -0
- package/dist/src/github/api.js +75 -0
- package/dist/src/github/api.js.map +1 -0
- package/dist/src/github/app-manifest.d.ts +28 -0
- package/dist/src/github/app-manifest.d.ts.map +1 -0
- package/dist/src/github/app-manifest.js +27 -0
- package/dist/src/github/app-manifest.js.map +1 -0
- package/dist/src/github/checks.d.ts +36 -0
- package/dist/src/github/checks.d.ts.map +1 -0
- package/dist/src/github/checks.js +90 -0
- package/dist/src/github/checks.js.map +1 -0
- package/dist/src/github/scanner.d.ts +20 -0
- package/dist/src/github/scanner.d.ts.map +1 -0
- package/dist/src/github/scanner.js +78 -0
- package/dist/src/github/scanner.js.map +1 -0
- package/dist/src/github/webhook.d.ts +39 -0
- package/dist/src/github/webhook.d.ts.map +1 -0
- package/dist/src/github/webhook.js +80 -0
- package/dist/src/github/webhook.js.map +1 -0
- package/dist/src/hooks/installer.d.ts +4 -0
- package/dist/src/hooks/installer.d.ts.map +1 -0
- package/dist/src/hooks/installer.js +146 -0
- package/dist/src/hooks/installer.js.map +1 -0
- package/dist/src/mcp/server.d.ts +2 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +96 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tools/check-package.d.ts +30 -0
- package/dist/src/mcp/tools/check-package.d.ts.map +1 -0
- package/dist/src/mcp/tools/check-package.js +196 -0
- package/dist/src/mcp/tools/check-package.js.map +1 -0
- package/dist/src/mcp/tools/fix.d.ts +41 -0
- package/dist/src/mcp/tools/fix.d.ts.map +1 -0
- package/dist/src/mcp/tools/fix.js +98 -0
- package/dist/src/mcp/tools/fix.js.map +1 -0
- package/dist/src/mcp/tools/graph-query.d.ts +7 -0
- package/dist/src/mcp/tools/graph-query.d.ts.map +1 -0
- package/dist/src/mcp/tools/graph-query.js +139 -0
- package/dist/src/mcp/tools/graph-query.js.map +1 -0
- package/dist/src/mcp/tools/production-errors.d.ts +23 -0
- package/dist/src/mcp/tools/production-errors.d.ts.map +1 -0
- package/dist/src/mcp/tools/production-errors.js +46 -0
- package/dist/src/mcp/tools/production-errors.js.map +1 -0
- package/dist/src/mcp/tools/scan.d.ts +7 -0
- package/dist/src/mcp/tools/scan.d.ts.map +1 -0
- package/dist/src/mcp/tools/scan.js +9 -0
- package/dist/src/mcp/tools/scan.js.map +1 -0
- package/dist/src/mcp/tools/status.d.ts +9 -0
- package/dist/src/mcp/tools/status.d.ts.map +1 -0
- package/dist/src/mcp/tools/status.js +18 -0
- package/dist/src/mcp/tools/status.js.map +1 -0
- package/dist/src/mcp/tools/verify-resolution.d.ts +12 -0
- package/dist/src/mcp/tools/verify-resolution.d.ts.map +1 -0
- package/dist/src/mcp/tools/verify-resolution.js +45 -0
- package/dist/src/mcp/tools/verify-resolution.js.map +1 -0
- package/dist/src/types.d.ts +136 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import { GLOBAL_DIR_NAME, CONFIG_FILE, DEFAULT_CONFIG, DEFAULT_API_URL } from '../constants.js';
|
|
6
|
+
/**
|
|
7
|
+
* Deep merge two objects. Source values override target values.
|
|
8
|
+
* Arrays are replaced, not concatenated.
|
|
9
|
+
*/
|
|
10
|
+
function deepMerge(target, source) {
|
|
11
|
+
const result = { ...target };
|
|
12
|
+
for (const key of Object.keys(source)) {
|
|
13
|
+
const sourceVal = source[key];
|
|
14
|
+
const targetVal = result[key];
|
|
15
|
+
if (sourceVal !== null &&
|
|
16
|
+
sourceVal !== undefined &&
|
|
17
|
+
typeof sourceVal === 'object' &&
|
|
18
|
+
!Array.isArray(sourceVal) &&
|
|
19
|
+
targetVal !== null &&
|
|
20
|
+
targetVal !== undefined &&
|
|
21
|
+
typeof targetVal === 'object' &&
|
|
22
|
+
!Array.isArray(targetVal)) {
|
|
23
|
+
result[key] = deepMerge(targetVal, sourceVal);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
result[key] = sourceVal;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Returns the path to the global ShipSafe config directory (~/.shipsafe).
|
|
33
|
+
*/
|
|
34
|
+
export function getGlobalConfigDir() {
|
|
35
|
+
return path.join(os.homedir(), GLOBAL_DIR_NAME);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Reads a JSON config file and returns the parsed contents, or an empty
|
|
39
|
+
* object if the file does not exist or cannot be parsed.
|
|
40
|
+
*/
|
|
41
|
+
async function readConfigFile(filePath) {
|
|
42
|
+
try {
|
|
43
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
44
|
+
return JSON.parse(raw);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Reads the global config from ~/.shipsafe/config.json.
|
|
52
|
+
* Returns defaults if the file is missing.
|
|
53
|
+
*/
|
|
54
|
+
export async function loadGlobalConfig() {
|
|
55
|
+
const configPath = path.join(getGlobalConfigDir(), 'config.json');
|
|
56
|
+
const raw = await readConfigFile(configPath);
|
|
57
|
+
return deepMerge({ ...DEFAULT_CONFIG }, raw);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Reads the project config from <projectDir>/shipsafe.config.json.
|
|
61
|
+
* Returns defaults if the file is missing.
|
|
62
|
+
*/
|
|
63
|
+
export async function loadProjectConfig(projectDir) {
|
|
64
|
+
const dir = projectDir ?? process.cwd();
|
|
65
|
+
const configPath = path.join(dir, CONFIG_FILE);
|
|
66
|
+
const raw = await readConfigFile(configPath);
|
|
67
|
+
return deepMerge({ ...DEFAULT_CONFIG }, raw);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Merges global and project configs. Project config overrides global config.
|
|
71
|
+
* Merge order: defaults < global < project.
|
|
72
|
+
*/
|
|
73
|
+
export async function loadConfig(projectDir) {
|
|
74
|
+
const globalConfigPath = path.join(getGlobalConfigDir(), 'config.json');
|
|
75
|
+
const dir = projectDir ?? process.cwd();
|
|
76
|
+
const projectConfigPath = path.join(dir, CONFIG_FILE);
|
|
77
|
+
const globalRaw = await readConfigFile(globalConfigPath);
|
|
78
|
+
const projectRaw = await readConfigFile(projectConfigPath);
|
|
79
|
+
// Three-way merge: defaults < global raw < project raw
|
|
80
|
+
const withGlobal = deepMerge({ ...DEFAULT_CONFIG }, globalRaw);
|
|
81
|
+
return deepMerge(withGlobal, projectRaw);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Returns the API endpoint. Priority: SHIPSAFE_API_URL env var > config value > default.
|
|
85
|
+
*/
|
|
86
|
+
export function getApiEndpoint(config) {
|
|
87
|
+
const envUrl = process.env.SHIPSAFE_API_URL?.trim();
|
|
88
|
+
if (envUrl)
|
|
89
|
+
return envUrl;
|
|
90
|
+
if (config?.apiEndpoint)
|
|
91
|
+
return config.apiEndpoint;
|
|
92
|
+
return DEFAULT_API_URL;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Writes config to ~/.shipsafe/config.json, creating the directory if needed.
|
|
96
|
+
*/
|
|
97
|
+
export async function saveGlobalConfig(config) {
|
|
98
|
+
const dir = getGlobalConfigDir();
|
|
99
|
+
await fs.mkdir(dir, { recursive: true });
|
|
100
|
+
const configPath = path.join(dir, 'config.json');
|
|
101
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Writes config to <projectDir>/shipsafe.config.json.
|
|
105
|
+
*/
|
|
106
|
+
export async function saveProjectConfig(config, projectDir) {
|
|
107
|
+
const dir = projectDir ?? process.cwd();
|
|
108
|
+
const configPath = path.join(dir, CONFIG_FILE);
|
|
109
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Derives project name from package.json name field or falls back to directory name.
|
|
113
|
+
*/
|
|
114
|
+
export function getProjectName(projectDir) {
|
|
115
|
+
const dir = projectDir ?? process.cwd();
|
|
116
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
117
|
+
try {
|
|
118
|
+
if (existsSync(pkgPath)) {
|
|
119
|
+
const raw = readFileSync(pkgPath, 'utf-8');
|
|
120
|
+
const pkg = JSON.parse(raw);
|
|
121
|
+
if (pkg.name) {
|
|
122
|
+
return pkg.name;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// fall through to directory name
|
|
128
|
+
}
|
|
129
|
+
return path.basename(dir);
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/config/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare const SHIPSAFE_DIR = ".shipsafe";
|
|
2
|
+
export declare const CONFIG_FILE = "shipsafe.config.json";
|
|
3
|
+
export declare const GLOBAL_DIR_NAME = ".shipsafe";
|
|
4
|
+
export declare const CLAUDE_MD_START = "<!-- shipsafe:start -->";
|
|
5
|
+
export declare const CLAUDE_MD_END = "<!-- shipsafe:end -->";
|
|
6
|
+
export declare const VERSION = "0.1.0";
|
|
7
|
+
export declare const HOOK_MARKER = "# SHIPSAFE_HOOK";
|
|
8
|
+
export declare const DEFAULT_API_URL = "http://localhost:3747";
|
|
9
|
+
export declare const EXIT_CODES: {
|
|
10
|
+
readonly SUCCESS: 0;
|
|
11
|
+
readonly SCAN_FAIL: 1;
|
|
12
|
+
readonly TOOL_MISSING: 2;
|
|
13
|
+
readonly CONFIG_ERROR: 3;
|
|
14
|
+
};
|
|
15
|
+
export declare const SEVERITY_ORDER: Record<string, number>;
|
|
16
|
+
export declare const DEFAULT_CONFIG: {
|
|
17
|
+
monitoring: {
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
error_sample_rate: number;
|
|
20
|
+
performance_sample_rate: number;
|
|
21
|
+
};
|
|
22
|
+
scan: {
|
|
23
|
+
ignore_paths: string[];
|
|
24
|
+
ignore_rules: string[];
|
|
25
|
+
severity_threshold: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,cAAc,CAAC;AACxC,eAAO,MAAM,WAAW,yBAAyB,CAAC;AAClD,eAAO,MAAM,eAAe,cAAc,CAAC;AAC3C,eAAO,MAAM,eAAe,4BAA4B,CAAC;AACzD,eAAO,MAAM,aAAa,0BAA0B,CAAC;AACrD,eAAO,MAAM,OAAO,UAAU,CAAC;AAC/B,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,eAAe,0BAA0B,CAAC;AAEvD,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMjD,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE;IAC3B,UAAU,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC;QAAC,uBAAuB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7F,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAC;CAYtF,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const SHIPSAFE_DIR = '.shipsafe';
|
|
2
|
+
export const CONFIG_FILE = 'shipsafe.config.json';
|
|
3
|
+
export const GLOBAL_DIR_NAME = '.shipsafe';
|
|
4
|
+
export const CLAUDE_MD_START = '<!-- shipsafe:start -->';
|
|
5
|
+
export const CLAUDE_MD_END = '<!-- shipsafe:end -->';
|
|
6
|
+
export const VERSION = '0.1.0';
|
|
7
|
+
export const HOOK_MARKER = '# SHIPSAFE_HOOK';
|
|
8
|
+
export const DEFAULT_API_URL = 'http://localhost:3747';
|
|
9
|
+
export const EXIT_CODES = {
|
|
10
|
+
SUCCESS: 0,
|
|
11
|
+
SCAN_FAIL: 1,
|
|
12
|
+
TOOL_MISSING: 2,
|
|
13
|
+
CONFIG_ERROR: 3,
|
|
14
|
+
};
|
|
15
|
+
export const SEVERITY_ORDER = {
|
|
16
|
+
critical: 0,
|
|
17
|
+
high: 1,
|
|
18
|
+
medium: 2,
|
|
19
|
+
low: 3,
|
|
20
|
+
info: 4,
|
|
21
|
+
};
|
|
22
|
+
export const DEFAULT_CONFIG = {
|
|
23
|
+
monitoring: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
error_sample_rate: 1.0,
|
|
26
|
+
performance_sample_rate: 1.0,
|
|
27
|
+
},
|
|
28
|
+
scan: {
|
|
29
|
+
ignore_paths: ['node_modules', 'dist', '.git', 'coverage'],
|
|
30
|
+
ignore_rules: [],
|
|
31
|
+
severity_threshold: 'high',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;AACxC,MAAM,CAAC,MAAM,WAAW,GAAG,sBAAsB,CAAC;AAClD,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAC3C,MAAM,CAAC,MAAM,eAAe,GAAG,yBAAyB,CAAC;AACzD,MAAM,CAAC,MAAM,aAAa,GAAG,uBAAuB,CAAC;AACrD,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAC/B,MAAM,CAAC,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAC7C,MAAM,CAAC,MAAM,eAAe,GAAG,uBAAuB,CAAC;AAEvD,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,OAAO,EAAE,CAAC;IACV,SAAS,EAAE,CAAC;IACZ,YAAY,EAAE,CAAC;IACf,YAAY,EAAE,CAAC;CACP,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAA2B;IACpD,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;IACN,IAAI,EAAE,CAAC;CACR,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAGvB;IACF,UAAU,EAAE;QACV,OAAO,EAAE,IAAI;QACb,iBAAiB,EAAE,GAAG;QACtB,uBAAuB,EAAE,GAAG;KAC7B;IACD,IAAI,EAAE;QACJ,YAAY,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC;QAC1D,YAAY,EAAE,EAAE;QAChB,kBAAkB,EAAE,MAAM;KAC3B;CACF,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { GraphStore } from './store.js';
|
|
2
|
+
export interface DataFlowResult {
|
|
3
|
+
source: {
|
|
4
|
+
name: string;
|
|
5
|
+
filePath: string;
|
|
6
|
+
line: number;
|
|
7
|
+
type: string;
|
|
8
|
+
};
|
|
9
|
+
sink: {
|
|
10
|
+
name: string;
|
|
11
|
+
filePath: string;
|
|
12
|
+
line: number;
|
|
13
|
+
type: string;
|
|
14
|
+
};
|
|
15
|
+
path: string[];
|
|
16
|
+
hasSanitization: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Classify a function name as a taint source type, or null if not a source. */
|
|
19
|
+
export declare function classifySource(name: string): string | null;
|
|
20
|
+
/** Classify a function name as a taint sink type, or null if not a sink. */
|
|
21
|
+
export declare function classifySink(name: string): string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Trace tainted data from source functions (user input, request params) through
|
|
24
|
+
* call chains to dangerous sinks (SQL, filesystem, shell) using BFS on the knowledge graph.
|
|
25
|
+
*
|
|
26
|
+
* Algorithm:
|
|
27
|
+
* 1. Find all functions in the graph.
|
|
28
|
+
* 2. Identify source functions (by name pattern).
|
|
29
|
+
* 3. For each source, find all callers (functions that call the source).
|
|
30
|
+
* These callers handle tainted data.
|
|
31
|
+
* 4. BFS from each tainted caller, following callees up to depth MAX_DEPTH.
|
|
32
|
+
* 5. If BFS reaches a sink, record a DataFlowResult.
|
|
33
|
+
* 6. Mark as hasSanitization=true if any function in the path is a sanitizer.
|
|
34
|
+
*/
|
|
35
|
+
export declare function findDataFlows(store: GraphStore): Promise<DataFlowResult[]>;
|
|
36
|
+
//# sourceMappingURL=data-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-flow.d.ts","sourceRoot":"","sources":["../../../../src/engines/graph/data-flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,YAAY,CAAC;AA6B5D,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,eAAe,EAAE,OAAO,CAAC;CAC1B;AAID,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ1D;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA4BxD;AASD;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAuIhF"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// ── Constants ──
|
|
2
|
+
const SOURCE_PATTERNS = [
|
|
3
|
+
'request',
|
|
4
|
+
'req',
|
|
5
|
+
'body',
|
|
6
|
+
'params',
|
|
7
|
+
'query',
|
|
8
|
+
'getrequestbody',
|
|
9
|
+
'readline',
|
|
10
|
+
'input',
|
|
11
|
+
'formdata',
|
|
12
|
+
];
|
|
13
|
+
const SINK_PATTERNS = {
|
|
14
|
+
database: ['query', 'execute', 'exec', 'find', 'insert', 'update', 'delete', 'raw'],
|
|
15
|
+
filesystem: ['writefile', 'spawn'],
|
|
16
|
+
shell: ['exec', 'spawn'],
|
|
17
|
+
eval: ['eval', 'function'],
|
|
18
|
+
};
|
|
19
|
+
const SANITIZER_PATTERNS = ['valid', 'sanitiz', 'escape', 'clean', 'encode', 'parameteriz', 'prepare'];
|
|
20
|
+
const MAX_DEPTH = 8;
|
|
21
|
+
// ── Exported classifier functions ──
|
|
22
|
+
/** Classify a function name as a taint source type, or null if not a source. */
|
|
23
|
+
export function classifySource(name) {
|
|
24
|
+
const nameLower = name.toLowerCase();
|
|
25
|
+
for (const pattern of SOURCE_PATTERNS) {
|
|
26
|
+
if (nameLower.includes(pattern)) {
|
|
27
|
+
return 'user_input';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
/** Classify a function name as a taint sink type, or null if not a sink. */
|
|
33
|
+
export function classifySink(name) {
|
|
34
|
+
const nameLower = name.toLowerCase();
|
|
35
|
+
// Prioritize more specific types: eval > shell > filesystem > database
|
|
36
|
+
// Check eval first
|
|
37
|
+
if (nameLower === 'eval' || nameLower === 'function') {
|
|
38
|
+
return 'eval';
|
|
39
|
+
}
|
|
40
|
+
// shell: spawn, exec (but not execute/execSync — those go to database/shell)
|
|
41
|
+
// exec is both shell and database — shell takes priority
|
|
42
|
+
if (nameLower === 'exec' || nameLower === 'execsync' || nameLower === 'spawn' || nameLower === 'execfile') {
|
|
43
|
+
return 'shell';
|
|
44
|
+
}
|
|
45
|
+
// filesystem
|
|
46
|
+
if (nameLower === 'writefile') {
|
|
47
|
+
return 'filesystem';
|
|
48
|
+
}
|
|
49
|
+
// database
|
|
50
|
+
for (const pattern of SINK_PATTERNS['database']) {
|
|
51
|
+
if (nameLower === pattern.toLowerCase()) {
|
|
52
|
+
return 'database';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
function isSanitizer(name) {
|
|
58
|
+
const nameLower = name.toLowerCase();
|
|
59
|
+
return SANITIZER_PATTERNS.some((p) => nameLower.includes(p));
|
|
60
|
+
}
|
|
61
|
+
// ── findDataFlows ──
|
|
62
|
+
/**
|
|
63
|
+
* Trace tainted data from source functions (user input, request params) through
|
|
64
|
+
* call chains to dangerous sinks (SQL, filesystem, shell) using BFS on the knowledge graph.
|
|
65
|
+
*
|
|
66
|
+
* Algorithm:
|
|
67
|
+
* 1. Find all functions in the graph.
|
|
68
|
+
* 2. Identify source functions (by name pattern).
|
|
69
|
+
* 3. For each source, find all callers (functions that call the source).
|
|
70
|
+
* These callers handle tainted data.
|
|
71
|
+
* 4. BFS from each tainted caller, following callees up to depth MAX_DEPTH.
|
|
72
|
+
* 5. If BFS reaches a sink, record a DataFlowResult.
|
|
73
|
+
* 6. Mark as hasSanitization=true if any function in the path is a sanitizer.
|
|
74
|
+
*/
|
|
75
|
+
export async function findDataFlows(store) {
|
|
76
|
+
// Step 1: Get all functions
|
|
77
|
+
const allFunctionsRaw = (await store.query('MATCH (fn:Function) RETURN fn.name AS name, fn.filePath AS filePath, fn.startLine AS line'));
|
|
78
|
+
if (allFunctionsRaw.length === 0) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
// Step 2: Identify source functions
|
|
82
|
+
const sourceFunctions = allFunctionsRaw.filter((fn) => {
|
|
83
|
+
const name = fn['name'];
|
|
84
|
+
return classifySource(name) !== null;
|
|
85
|
+
});
|
|
86
|
+
const results = [];
|
|
87
|
+
// Track source+sink pairs to avoid duplicates
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
// Step 3 & 4: For each source, find callers and BFS their callees to find sinks
|
|
90
|
+
for (const sourceFn of sourceFunctions) {
|
|
91
|
+
const sourceName = sourceFn['name'];
|
|
92
|
+
const sourceType = classifySource(sourceName);
|
|
93
|
+
// Find all functions that call this source (i.e., functions that handle tainted data)
|
|
94
|
+
const callers = (await store.getCallers(sourceName, 1));
|
|
95
|
+
for (const caller of callers) {
|
|
96
|
+
// Pre-compute whether any function reachable from caller (within MAX_DEPTH)
|
|
97
|
+
// is a sanitizer. This captures sibling calls like:
|
|
98
|
+
// safeHandler -> sanitizeInput (sanitizer)
|
|
99
|
+
// safeHandler -> exec (sink)
|
|
100
|
+
// Even though sanitizeInput is not on the path to exec, it shows sanitization intent.
|
|
101
|
+
const allCalleesOfCaller = (await store.getCallees(caller.name, MAX_DEPTH));
|
|
102
|
+
const callerHasSanitizerInScope = isSanitizer(caller.name) ||
|
|
103
|
+
allCalleesOfCaller.some((fn) => isSanitizer(fn.name));
|
|
104
|
+
const queue = [
|
|
105
|
+
{
|
|
106
|
+
fn: caller,
|
|
107
|
+
path: [sourceName, caller.name],
|
|
108
|
+
depth: 1,
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
const visited = new Set();
|
|
112
|
+
visited.add(sourceName);
|
|
113
|
+
visited.add(caller.name);
|
|
114
|
+
while (queue.length > 0) {
|
|
115
|
+
const node = queue.shift();
|
|
116
|
+
const { fn, path, depth } = node;
|
|
117
|
+
// Check if current function is itself a sink
|
|
118
|
+
const sinkType = classifySink(fn.name);
|
|
119
|
+
if (sinkType !== null) {
|
|
120
|
+
const key = `${sourceName}:${fn.name}`;
|
|
121
|
+
if (!seen.has(key)) {
|
|
122
|
+
seen.add(key);
|
|
123
|
+
results.push({
|
|
124
|
+
source: {
|
|
125
|
+
name: sourceName,
|
|
126
|
+
filePath: sourceFn['filePath'],
|
|
127
|
+
line: sourceFn['line'],
|
|
128
|
+
type: sourceType,
|
|
129
|
+
},
|
|
130
|
+
sink: {
|
|
131
|
+
name: fn.name,
|
|
132
|
+
filePath: fn.filePath,
|
|
133
|
+
line: fn.startLine,
|
|
134
|
+
type: sinkType,
|
|
135
|
+
},
|
|
136
|
+
path,
|
|
137
|
+
hasSanitization: callerHasSanitizerInScope,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// Don't continue BFS past a sink
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (depth >= MAX_DEPTH)
|
|
144
|
+
continue;
|
|
145
|
+
// Get direct callees of current function
|
|
146
|
+
const callees = (await store.getCallees(fn.name, 1));
|
|
147
|
+
for (const callee of callees) {
|
|
148
|
+
if (visited.has(callee.name))
|
|
149
|
+
continue;
|
|
150
|
+
visited.add(callee.name);
|
|
151
|
+
const calleeSinkType = classifySink(callee.name);
|
|
152
|
+
const newPath = [...path, callee.name];
|
|
153
|
+
if (calleeSinkType !== null) {
|
|
154
|
+
// Found a sink!
|
|
155
|
+
const key = `${sourceName}:${callee.name}`;
|
|
156
|
+
if (!seen.has(key)) {
|
|
157
|
+
seen.add(key);
|
|
158
|
+
results.push({
|
|
159
|
+
source: {
|
|
160
|
+
name: sourceName,
|
|
161
|
+
filePath: sourceFn['filePath'],
|
|
162
|
+
line: sourceFn['line'],
|
|
163
|
+
type: sourceType,
|
|
164
|
+
},
|
|
165
|
+
sink: {
|
|
166
|
+
name: callee.name,
|
|
167
|
+
filePath: callee.filePath,
|
|
168
|
+
line: callee.startLine,
|
|
169
|
+
type: calleeSinkType,
|
|
170
|
+
},
|
|
171
|
+
path: newPath,
|
|
172
|
+
hasSanitization: callerHasSanitizerInScope,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
queue.push({
|
|
178
|
+
fn: callee,
|
|
179
|
+
path: newPath,
|
|
180
|
+
depth: depth + 1,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return results;
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=data-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-flow.js","sourceRoot":"","sources":["../../../../src/engines/graph/data-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Finding, ScanScope } from '../../types.js';
|
|
2
|
+
export interface GraphEngineResult {
|
|
3
|
+
findings: Finding[];
|
|
4
|
+
stats: {
|
|
5
|
+
filesScanned: number;
|
|
6
|
+
functionsFound: number;
|
|
7
|
+
classesFound: number;
|
|
8
|
+
callEdges: number;
|
|
9
|
+
attackPathsFound: number;
|
|
10
|
+
};
|
|
11
|
+
duration_ms: number;
|
|
12
|
+
}
|
|
13
|
+
/** Check if graph engine dependencies are available (tree-sitter, etc.) */
|
|
14
|
+
export declare function isGraphEngineAvailable(): boolean;
|
|
15
|
+
/** Run the full Knowledge Graph analysis on a project. */
|
|
16
|
+
export declare function runGraphEngine(options: {
|
|
17
|
+
targetPath: string;
|
|
18
|
+
scope: ScanScope;
|
|
19
|
+
}): Promise<GraphEngineResult>;
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/engines/graph/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAazD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE;QACL,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,2EAA2E;AAC3E,wBAAgB,sBAAsB,IAAI,OAAO,CAOhD;AAED,0DAA0D;AAC1D,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;CAClB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAyF7B"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { rm, mkdir } from 'node:fs/promises';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { initParser, parseProject } from './parser.js';
|
|
6
|
+
import { createGraphStore } from './store.js';
|
|
7
|
+
import { findAttackPaths, findBlastRadius, findMissingAuth, queryResultsToFindings, } from './queries.js';
|
|
8
|
+
import { findDataFlows } from './data-flow.js';
|
|
9
|
+
// ── Public API ──
|
|
10
|
+
/** Check if graph engine dependencies are available (tree-sitter, etc.) */
|
|
11
|
+
export function isGraphEngineAvailable() {
|
|
12
|
+
try {
|
|
13
|
+
// web-tree-sitter is a bundled dependency, so if we got here it's available
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Run the full Knowledge Graph analysis on a project. */
|
|
21
|
+
export async function runGraphEngine(options) {
|
|
22
|
+
const startTime = Date.now();
|
|
23
|
+
const { targetPath } = options;
|
|
24
|
+
// 1. Initialize the parser
|
|
25
|
+
await initParser();
|
|
26
|
+
// 2. Parse project files
|
|
27
|
+
const parsedFiles = await parseProject(targetPath);
|
|
28
|
+
// 3. Create a temporary graph store
|
|
29
|
+
const tmpDir = path.join(os.tmpdir(), `shipsafe-graph-${randomUUID()}`);
|
|
30
|
+
await mkdir(tmpDir, { recursive: true });
|
|
31
|
+
const store = await createGraphStore(tmpDir);
|
|
32
|
+
try {
|
|
33
|
+
// 4. Build the graph from parsed files
|
|
34
|
+
await store.buildGraph(parsedFiles);
|
|
35
|
+
// 5. Compute stats
|
|
36
|
+
const totalFunctions = parsedFiles.reduce((sum, f) => sum + f.functions.length, 0);
|
|
37
|
+
const totalClasses = parsedFiles.reduce((sum, f) => sum + f.classes.length, 0);
|
|
38
|
+
const totalCallEdges = parsedFiles.reduce((sum, f) => sum + f.callSites.length, 0);
|
|
39
|
+
// 6. Run all security queries
|
|
40
|
+
const attackPaths = await findAttackPaths(store);
|
|
41
|
+
// Find blast radius for any known-vulnerable functions (sinks)
|
|
42
|
+
const sinkNames = new Set();
|
|
43
|
+
for (const ap of attackPaths) {
|
|
44
|
+
if (!ap.hasValidation) {
|
|
45
|
+
sinkNames.add(ap.sink.name);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const blastRadiusResults = [];
|
|
49
|
+
for (const sinkName of sinkNames) {
|
|
50
|
+
const br = await findBlastRadius(store, sinkName);
|
|
51
|
+
blastRadiusResults.push(br);
|
|
52
|
+
}
|
|
53
|
+
const missingAuth = await findMissingAuth(store);
|
|
54
|
+
// 7. Convert query results to findings
|
|
55
|
+
const findings = queryResultsToFindings(attackPaths, blastRadiusResults, missingAuth);
|
|
56
|
+
// 8. Run data flow taint analysis and add tainted_data_flow findings
|
|
57
|
+
const dataFlows = await findDataFlows(store);
|
|
58
|
+
let dfIdCounter = 0;
|
|
59
|
+
for (const flow of dataFlows) {
|
|
60
|
+
if (flow.hasSanitization)
|
|
61
|
+
continue; // sanitized flows are not findings
|
|
62
|
+
dfIdCounter++;
|
|
63
|
+
const severity = flow.sink.type === 'shell' || flow.sink.type === 'eval' ? 'critical' : 'high';
|
|
64
|
+
findings.push({
|
|
65
|
+
id: `kg-tainted-flow-${dfIdCounter}`,
|
|
66
|
+
engine: 'knowledge_graph',
|
|
67
|
+
severity,
|
|
68
|
+
type: 'tainted_data_flow',
|
|
69
|
+
file: flow.source.filePath,
|
|
70
|
+
line: flow.source.line,
|
|
71
|
+
description: `Tainted data flows from ${flow.source.name} (${flow.source.type}) to ${flow.sink.name} (${flow.sink.type} sink): ${flow.path.join(' -> ')}`,
|
|
72
|
+
fix_suggestion: `Add input sanitization or parameterization between ${flow.source.name} and ${flow.sink.name}`,
|
|
73
|
+
auto_fixable: false,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// 9. Return findings with stats and timing
|
|
77
|
+
return {
|
|
78
|
+
findings,
|
|
79
|
+
stats: {
|
|
80
|
+
filesScanned: parsedFiles.length,
|
|
81
|
+
functionsFound: totalFunctions,
|
|
82
|
+
classesFound: totalClasses,
|
|
83
|
+
callEdges: totalCallEdges,
|
|
84
|
+
attackPathsFound: attackPaths.length,
|
|
85
|
+
},
|
|
86
|
+
duration_ms: Date.now() - startTime,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
// 10. Clean up (close graph store and remove temp directory)
|
|
91
|
+
await store.close();
|
|
92
|
+
try {
|
|
93
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Best-effort cleanup
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/engines/graph/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACL,eAAe,EACf,eAAe,EACf,eAAe,EACf,sBAAsB,GACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAgB/C,mBAAmB;AAEnB,2EAA2E;AAC3E,MAAM,UAAU,sBAAsB;IACpC,IAAI,CAAC;QACH,4EAA4E;QAC5E,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAGpC;IACC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC;IAE/B,2BAA2B;IAC3B,MAAM,UAAU,EAAE,CAAC;IAEnB,yBAAyB;IACzB,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,CAAC;IAEnD,oCAAoC;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,kBAAkB,UAAU,EAAE,EAAE,CAAC,CAAC;IACxE,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAE7C,IAAI,CAAC;QACH,uCAAuC;QACvC,MAAM,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QAEpC,mBAAmB;QACnB,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACnF,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/E,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAEnF,8BAA8B;QAC9B,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,CAAC;QAEjD,+DAA+D;QAC/D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QACpC,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;YAC7B,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC;gBACtB,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;QACD,MAAM,kBAAkB,GAAG,EAAE,CAAC;QAC9B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,EAAE,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAClD,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9B,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,CAAC;QAEjD,uCAAuC;QACvC,MAAM,QAAQ,GAAG,sBAAsB,CAAC,WAAW,EAAE,kBAAkB,EAAE,WAAW,CAAC,CAAC;QAEtF,qEAAqE;QACrE,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,eAAe;gBAAE,SAAS,CAAC,mCAAmC;YAEvE,WAAW,EAAE,CAAC;YACd,MAAM,QAAQ,GACZ,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;YAEhF,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,mBAAmB,WAAW,EAAE;gBACpC,MAAM,EAAE,iBAAiB;gBACzB,QAAQ;gBACR,IAAI,EAAE,mBAAmB;gBACzB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAC1B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;gBACtB,WAAW,EAAE,2BAA2B,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,IAAI,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;gBACzJ,cAAc,EAAE,sDAAsD,IAAI,CAAC,MAAM,CAAC,IAAI,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;gBAC9G,YAAY,EAAE,KAAK;aACpB,CAAC,CAAC;QACL,CAAC;QAED,2CAA2C;QAC3C,OAAO;YACL,QAAQ;YACR,KAAK,EAAE;gBACL,YAAY,EAAE,WAAW,CAAC,MAAM;gBAChC,cAAc,EAAE,cAAc;gBAC9B,YAAY,EAAE,YAAY;gBAC1B,SAAS,EAAE,cAAc;gBACzB,gBAAgB,EAAE,WAAW,CAAC,MAAM;aACrC;YACD,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;SACpC,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,6DAA6D;QAC7D,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SupportedLanguage, ParsedFile } from '../../types.js';
|
|
2
|
+
/** Initialize tree-sitter. Must be called once before parsing. */
|
|
3
|
+
export declare function initParser(): Promise<void>;
|
|
4
|
+
/** Detect language from file extension. Returns null for unsupported files. */
|
|
5
|
+
export declare function detectLanguage(filePath: string): SupportedLanguage | null;
|
|
6
|
+
/** Parse a single file and extract structural nodes. */
|
|
7
|
+
export declare function parseFile(filePath: string, content: string): Promise<ParsedFile>;
|
|
8
|
+
/** Parse all supported files in a directory. */
|
|
9
|
+
export declare function parseProject(projectDir: string, options?: {
|
|
10
|
+
include?: string[];
|
|
11
|
+
exclude?: string[];
|
|
12
|
+
}): Promise<ParsedFile[]>;
|
|
13
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../../src/engines/graph/parser.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EAMX,MAAM,gBAAgB,CAAC;AAWxB,kEAAkE;AAClE,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAGhD;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAgBzE;AAED,wDAAwD;AACxD,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAiCtF;AAED,gDAAgD;AAChD,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACnD,OAAO,CAAC,UAAU,EAAE,CAAC,CA0BvB"}
|