depwire-cli 0.9.26 → 0.9.28
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 +32 -2
- package/dist/{chunk-YYY5TNG7.js → chunk-ITEGMPF7.js} +576 -104
- package/dist/{chunk-B2KGFBZL.js → chunk-VJLBOFCD.js} +36 -22
- package/dist/index.js +47 -44
- package/dist/mcpb-entry.js +3 -3
- package/dist/sdk.d.ts +31 -2
- package/dist/sdk.js +3 -1
- package/package.json +6 -6
|
@@ -106,7 +106,7 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
106
106
|
|
|
107
107
|
// src/parser/index.ts
|
|
108
108
|
import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
109
|
-
import { join as join8 } from "path";
|
|
109
|
+
import { join as join8, resolve as resolve3 } from "path";
|
|
110
110
|
|
|
111
111
|
// src/parser/detect.ts
|
|
112
112
|
import { extname as extname3 } from "path";
|
|
@@ -1578,7 +1578,7 @@ var javascriptParser = {
|
|
|
1578
1578
|
|
|
1579
1579
|
// src/parser/go.ts
|
|
1580
1580
|
import { existsSync as existsSync5, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
1581
|
-
import { join as join5, dirname as dirname4 } from "path";
|
|
1581
|
+
import { join as join5, dirname as dirname4, resolve as resolve2 } from "path";
|
|
1582
1582
|
function parseGoFile(filePath, sourceCode, projectRoot) {
|
|
1583
1583
|
const parser = getParser("go");
|
|
1584
1584
|
const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
@@ -1860,7 +1860,7 @@ function processCallExpression4(node, context) {
|
|
|
1860
1860
|
function readGoModuleName(projectRoot) {
|
|
1861
1861
|
let currentDir = projectRoot;
|
|
1862
1862
|
for (let i = 0; i < 5; i++) {
|
|
1863
|
-
const goModPath =
|
|
1863
|
+
const goModPath = resolve2(currentDir, "go.mod");
|
|
1864
1864
|
if (existsSync5(goModPath)) {
|
|
1865
1865
|
try {
|
|
1866
1866
|
const content = readFileSync2(goModPath, "utf-8");
|
|
@@ -2822,6 +2822,10 @@ async function parseProject(projectRoot, options) {
|
|
|
2822
2822
|
for (const file of files) {
|
|
2823
2823
|
try {
|
|
2824
2824
|
const fullPath = join8(projectRoot, file);
|
|
2825
|
+
if (!resolve3(fullPath).startsWith(resolve3(projectRoot))) {
|
|
2826
|
+
skippedFiles++;
|
|
2827
|
+
continue;
|
|
2828
|
+
}
|
|
2825
2829
|
if (options?.exclude) {
|
|
2826
2830
|
const shouldExclude2 = options.exclude.some(
|
|
2827
2831
|
(pattern) => minimatch(file, pattern, { matchBase: true })
|
|
@@ -2869,9 +2873,449 @@ async function parseProject(projectRoot, options) {
|
|
|
2869
2873
|
return parsedFiles;
|
|
2870
2874
|
}
|
|
2871
2875
|
|
|
2876
|
+
// src/cross-language/detectors/rest-api.ts
|
|
2877
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
2878
|
+
import { join as join9, resolve as resolve4 } from "path";
|
|
2879
|
+
function getLanguage(filePath) {
|
|
2880
|
+
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
|
|
2881
|
+
if (filePath.endsWith(".js") || filePath.endsWith(".jsx") || filePath.endsWith(".mjs") || filePath.endsWith(".cjs")) return "javascript";
|
|
2882
|
+
if (filePath.endsWith(".py")) return "python";
|
|
2883
|
+
if (filePath.endsWith(".go")) return "go";
|
|
2884
|
+
return "unknown";
|
|
2885
|
+
}
|
|
2886
|
+
function normalizePath(routePath) {
|
|
2887
|
+
return routePath.replace(/:[a-zA-Z_][a-zA-Z0-9_]*/g, "__PARAM__").replace(/\{[a-zA-Z_][a-zA-Z0-9_]*\}/g, "__PARAM__");
|
|
2888
|
+
}
|
|
2889
|
+
function stripTrailingSlash(p) {
|
|
2890
|
+
return p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
|
|
2891
|
+
}
|
|
2892
|
+
function extractHttpCalls(source, filePath) {
|
|
2893
|
+
const calls = [];
|
|
2894
|
+
const lines = source.split("\n");
|
|
2895
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2896
|
+
const line = lines[i];
|
|
2897
|
+
const fetchMatch = line.match(/fetch\s*\(\s*(['"`])([^'"`]+)\1/);
|
|
2898
|
+
if (fetchMatch) {
|
|
2899
|
+
const path6 = fetchMatch[2];
|
|
2900
|
+
if (isLocalApiPath(path6)) {
|
|
2901
|
+
const methodMatch = line.match(/method\s*:\s*['"](\w+)['"]/);
|
|
2902
|
+
const method = methodMatch ? methodMatch[1].toUpperCase() : "GET";
|
|
2903
|
+
calls.push({ method, path: cleanPath(path6), file: filePath, line: i + 1 });
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
if (!fetchMatch) {
|
|
2907
|
+
const fetchTemplateMatch = line.match(/fetch\s*\(\s*`([^`]+)`/);
|
|
2908
|
+
if (fetchTemplateMatch) {
|
|
2909
|
+
const path6 = fetchTemplateMatch[1];
|
|
2910
|
+
if (isLocalApiPath(path6)) {
|
|
2911
|
+
const methodMatch = line.match(/method\s*:\s*['"](\w+)['"]/);
|
|
2912
|
+
const method = methodMatch ? methodMatch[1].toUpperCase() : "GET";
|
|
2913
|
+
calls.push({ method, path: cleanPath(path6), file: filePath, line: i + 1 });
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
const axiosMatch = line.match(/axios\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"`])([^'"`]+)\2/i);
|
|
2918
|
+
if (axiosMatch) {
|
|
2919
|
+
const path6 = axiosMatch[3];
|
|
2920
|
+
if (isLocalApiPath(path6)) {
|
|
2921
|
+
calls.push({ method: axiosMatch[1].toUpperCase(), path: cleanPath(path6), file: filePath, line: i + 1 });
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
if (!axiosMatch) {
|
|
2925
|
+
const axiosTemplateMatch = line.match(/axios\s*\.\s*(get|post|put|delete|patch)\s*\(\s*`([^`]+)`/i);
|
|
2926
|
+
if (axiosTemplateMatch) {
|
|
2927
|
+
const path6 = axiosTemplateMatch[2];
|
|
2928
|
+
if (isLocalApiPath(path6)) {
|
|
2929
|
+
calls.push({ method: axiosTemplateMatch[1].toUpperCase(), path: cleanPath(path6), file: filePath, line: i + 1 });
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
const genericMatch = line.match(/\w+\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"`])([^'"`]+)\2/i);
|
|
2934
|
+
if (genericMatch && !line.match(/axios/) && !line.match(/app\s*\./) && !line.match(/router\s*\./) && !line.match(/r\s*\./)) {
|
|
2935
|
+
const path6 = genericMatch[3];
|
|
2936
|
+
if (isLocalApiPath(path6)) {
|
|
2937
|
+
calls.push({ method: genericMatch[1].toUpperCase(), path: cleanPath(path6), file: filePath, line: i + 1 });
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
return calls;
|
|
2942
|
+
}
|
|
2943
|
+
function isLocalApiPath(path6) {
|
|
2944
|
+
if (path6.startsWith("http://") || path6.startsWith("https://")) return false;
|
|
2945
|
+
return path6.startsWith("/") || path6.includes("/api/");
|
|
2946
|
+
}
|
|
2947
|
+
function cleanPath(path6) {
|
|
2948
|
+
let cleaned = path6.replace(/\$\{[^}]*\}/g, "");
|
|
2949
|
+
cleaned = stripTrailingSlash(cleaned);
|
|
2950
|
+
return cleaned;
|
|
2951
|
+
}
|
|
2952
|
+
function extractRouteDefinitions(source, filePath) {
|
|
2953
|
+
const routes = [];
|
|
2954
|
+
const lines = source.split("\n");
|
|
2955
|
+
const lang = getLanguage(filePath);
|
|
2956
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2957
|
+
const line = lines[i];
|
|
2958
|
+
if (lang === "typescript" || lang === "javascript") {
|
|
2959
|
+
const expressMatch = line.match(/(?:app|router)\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"`])([^'"`]+)\2/i);
|
|
2960
|
+
if (expressMatch) {
|
|
2961
|
+
const path6 = expressMatch[3];
|
|
2962
|
+
if (path6.startsWith("/")) {
|
|
2963
|
+
routes.push({
|
|
2964
|
+
method: expressMatch[1].toUpperCase(),
|
|
2965
|
+
path: path6,
|
|
2966
|
+
normalizedPath: normalizePath(path6),
|
|
2967
|
+
file: filePath,
|
|
2968
|
+
line: i + 1
|
|
2969
|
+
});
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
if (lang === "python") {
|
|
2974
|
+
const pythonMatch = line.match(/@(?:app|router)\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"])([^'"]+)\2/i);
|
|
2975
|
+
if (pythonMatch) {
|
|
2976
|
+
const path6 = pythonMatch[3];
|
|
2977
|
+
if (path6.startsWith("/")) {
|
|
2978
|
+
routes.push({
|
|
2979
|
+
method: pythonMatch[1].toUpperCase(),
|
|
2980
|
+
path: path6,
|
|
2981
|
+
normalizedPath: normalizePath(path6),
|
|
2982
|
+
file: filePath,
|
|
2983
|
+
line: i + 1
|
|
2984
|
+
});
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
const flaskMatch = line.match(/@(?:app|blueprint|router)\s*\.\s*route\s*\(\s*(['"])([^'"]+)\1/);
|
|
2988
|
+
if (flaskMatch) {
|
|
2989
|
+
const path6 = flaskMatch[2];
|
|
2990
|
+
if (path6.startsWith("/")) {
|
|
2991
|
+
const methodsMatch = line.match(/methods\s*=\s*\[([^\]]+)\]/);
|
|
2992
|
+
const methods = methodsMatch ? methodsMatch[1].match(/['"](\w+)['"]/g)?.map((m) => m.replace(/['"]/g, "").toUpperCase()) || ["GET"] : ["GET"];
|
|
2993
|
+
for (const method of methods) {
|
|
2994
|
+
routes.push({
|
|
2995
|
+
method,
|
|
2996
|
+
path: path6,
|
|
2997
|
+
normalizedPath: normalizePath(path6),
|
|
2998
|
+
file: filePath,
|
|
2999
|
+
line: i + 1
|
|
3000
|
+
});
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
if (lang === "go") {
|
|
3006
|
+
const goMatch = line.match(/(?:r|router|group)\s*\.\s*(GET|POST|PUT|DELETE|PATCH)\s*\(\s*"([^"]+)"/);
|
|
3007
|
+
if (goMatch) {
|
|
3008
|
+
const path6 = goMatch[2];
|
|
3009
|
+
if (path6.startsWith("/")) {
|
|
3010
|
+
routes.push({
|
|
3011
|
+
method: goMatch[1].toUpperCase(),
|
|
3012
|
+
path: path6,
|
|
3013
|
+
normalizedPath: normalizePath(path6),
|
|
3014
|
+
file: filePath,
|
|
3015
|
+
line: i + 1
|
|
3016
|
+
});
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
return routes;
|
|
3022
|
+
}
|
|
3023
|
+
function matchPaths(callPath, routeNormalized) {
|
|
3024
|
+
const normalizedCall = normalizePath(stripTrailingSlash(callPath));
|
|
3025
|
+
const normalizedRoute = stripTrailingSlash(routeNormalized);
|
|
3026
|
+
if (normalizedCall === normalizedRoute) return true;
|
|
3027
|
+
if (normalizedRoute.startsWith(normalizedCall) && normalizedRoute[normalizedCall.length] === "/") return true;
|
|
3028
|
+
const callParts = normalizedCall.split("/");
|
|
3029
|
+
const routeParts = normalizedRoute.split("/");
|
|
3030
|
+
if (callParts.length <= routeParts.length) {
|
|
3031
|
+
let match = true;
|
|
3032
|
+
for (let i = 0; i < callParts.length; i++) {
|
|
3033
|
+
if (routeParts[i] === "__PARAM__") continue;
|
|
3034
|
+
if (callParts[i] !== routeParts[i]) {
|
|
3035
|
+
match = false;
|
|
3036
|
+
break;
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
if (match) return true;
|
|
3040
|
+
}
|
|
3041
|
+
return false;
|
|
3042
|
+
}
|
|
3043
|
+
function getConfidence(callPath, callMethod, routePath, routeMethod) {
|
|
3044
|
+
const normalizedCall = normalizePath(stripTrailingSlash(callPath));
|
|
3045
|
+
const normalizedRoute = normalizePath(stripTrailingSlash(routePath));
|
|
3046
|
+
const exactPath = normalizedCall === normalizedRoute;
|
|
3047
|
+
const methodMatch = callMethod === routeMethod;
|
|
3048
|
+
if (exactPath && methodMatch) return "high";
|
|
3049
|
+
if (exactPath) return "medium";
|
|
3050
|
+
if (methodMatch) return "medium";
|
|
3051
|
+
return "low";
|
|
3052
|
+
}
|
|
3053
|
+
function detectRestApiEdges(files, projectRoot) {
|
|
3054
|
+
const edges = [];
|
|
3055
|
+
const allCalls = [];
|
|
3056
|
+
const allRoutes = [];
|
|
3057
|
+
for (const file of files) {
|
|
3058
|
+
const fullPath = join9(projectRoot, file.filePath);
|
|
3059
|
+
if (!resolve4(fullPath).startsWith(resolve4(projectRoot))) continue;
|
|
3060
|
+
let source;
|
|
3061
|
+
try {
|
|
3062
|
+
source = readFileSync6(fullPath, "utf-8");
|
|
3063
|
+
} catch {
|
|
3064
|
+
continue;
|
|
3065
|
+
}
|
|
3066
|
+
const lang = getLanguage(file.filePath);
|
|
3067
|
+
if (lang === "typescript" || lang === "javascript") {
|
|
3068
|
+
allCalls.push(...extractHttpCalls(source, file.filePath));
|
|
3069
|
+
}
|
|
3070
|
+
allRoutes.push(...extractRouteDefinitions(source, file.filePath));
|
|
3071
|
+
}
|
|
3072
|
+
for (const call of allCalls) {
|
|
3073
|
+
for (const route of allRoutes) {
|
|
3074
|
+
if (call.file === route.file) continue;
|
|
3075
|
+
if (matchPaths(call.path, route.normalizedPath)) {
|
|
3076
|
+
const confidence = getConfidence(call.path, call.method, route.path, route.method);
|
|
3077
|
+
edges.push({
|
|
3078
|
+
sourceFile: call.file,
|
|
3079
|
+
targetFile: route.file,
|
|
3080
|
+
edgeType: "rest-api",
|
|
3081
|
+
confidence,
|
|
3082
|
+
sourceLanguage: getLanguage(call.file),
|
|
3083
|
+
targetLanguage: getLanguage(route.file),
|
|
3084
|
+
sourceLine: call.line,
|
|
3085
|
+
targetLine: route.line,
|
|
3086
|
+
metadata: {
|
|
3087
|
+
httpMethod: call.method,
|
|
3088
|
+
path: call.path
|
|
3089
|
+
}
|
|
3090
|
+
});
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
return edges;
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
// src/cross-language/detectors/subprocess.ts
|
|
3098
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
3099
|
+
import { join as join10, resolve as resolve5, basename } from "path";
|
|
3100
|
+
var SCRIPT_EXTENSIONS = [".py", ".js", ".ts", ".go", ".rs"];
|
|
3101
|
+
function getLanguage2(filePath) {
|
|
3102
|
+
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
|
|
3103
|
+
if (filePath.endsWith(".js") || filePath.endsWith(".jsx") || filePath.endsWith(".mjs") || filePath.endsWith(".cjs")) return "javascript";
|
|
3104
|
+
if (filePath.endsWith(".py")) return "python";
|
|
3105
|
+
if (filePath.endsWith(".go")) return "go";
|
|
3106
|
+
if (filePath.endsWith(".rs")) return "rust";
|
|
3107
|
+
return "unknown";
|
|
3108
|
+
}
|
|
3109
|
+
function extractFilenameFromArgs(args) {
|
|
3110
|
+
const tokens = args.split(/[\s,'"[\]]+/).filter(Boolean);
|
|
3111
|
+
for (const token of tokens) {
|
|
3112
|
+
for (const ext of SCRIPT_EXTENSIONS) {
|
|
3113
|
+
if (token.endsWith(ext)) {
|
|
3114
|
+
return token;
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
return null;
|
|
3119
|
+
}
|
|
3120
|
+
function extractSubprocessCalls(source, filePath) {
|
|
3121
|
+
const calls = [];
|
|
3122
|
+
const lines = source.split("\n");
|
|
3123
|
+
const lang = getLanguage2(filePath);
|
|
3124
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3125
|
+
const line = lines[i];
|
|
3126
|
+
if (lang === "typescript" || lang === "javascript") {
|
|
3127
|
+
const execMatch = line.match(/(?:execSync|exec)\s*\(\s*(['"`])([^'"`]+)\1/);
|
|
3128
|
+
if (execMatch) {
|
|
3129
|
+
const command = execMatch[2];
|
|
3130
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3131
|
+
if (calledFile) {
|
|
3132
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
if (!execMatch) {
|
|
3136
|
+
const execTemplateMatch = line.match(/(?:execSync|exec)\s*\(\s*`([^`]+)`/);
|
|
3137
|
+
if (execTemplateMatch) {
|
|
3138
|
+
const command = execTemplateMatch[1].replace(/\$\{[^}]*\}/g, "");
|
|
3139
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3140
|
+
if (calledFile) {
|
|
3141
|
+
calls.push({ file: filePath, line: i + 1, command: execTemplateMatch[1], calledFile });
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
const spawnMatch = line.match(/(?:spawn|spawnSync)\s*\(\s*['"](\w+)['"]\s*,\s*\[([^\]]*)\]/);
|
|
3146
|
+
if (spawnMatch) {
|
|
3147
|
+
const command = `${spawnMatch[1]} ${spawnMatch[2]}`;
|
|
3148
|
+
const calledFile = extractFilenameFromArgs(spawnMatch[2]);
|
|
3149
|
+
if (calledFile) {
|
|
3150
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
if (lang === "python") {
|
|
3155
|
+
const subprocessMatch = line.match(/subprocess\s*\.\s*(?:run|call|Popen|check_output|check_call)\s*\(\s*\[([^\]]*)\]/);
|
|
3156
|
+
if (subprocessMatch) {
|
|
3157
|
+
const command = subprocessMatch[1];
|
|
3158
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3159
|
+
if (calledFile) {
|
|
3160
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
const osMatch = line.match(/os\s*\.\s*system\s*\(\s*['"]([^'"]+)['"]/);
|
|
3164
|
+
if (osMatch) {
|
|
3165
|
+
const command = osMatch[1];
|
|
3166
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3167
|
+
if (calledFile) {
|
|
3168
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
const subprocessStrMatch = line.match(/subprocess\s*\.\s*(?:run|call|Popen|check_output|check_call)\s*\(\s*['"]([^'"]+)['"]/);
|
|
3172
|
+
if (subprocessStrMatch) {
|
|
3173
|
+
const command = subprocessStrMatch[1];
|
|
3174
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3175
|
+
if (calledFile) {
|
|
3176
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
if (lang === "go") {
|
|
3181
|
+
const goMatch = line.match(/exec\s*\.\s*Command\s*\(\s*"([^"]+)"\s*,\s*"([^"]+)"/);
|
|
3182
|
+
if (goMatch) {
|
|
3183
|
+
const command = `${goMatch[1]} ${goMatch[2]}`;
|
|
3184
|
+
const calledFile = extractFilenameFromArgs(command);
|
|
3185
|
+
if (calledFile) {
|
|
3186
|
+
calls.push({ file: filePath, line: i + 1, command, calledFile });
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
return calls;
|
|
3192
|
+
}
|
|
3193
|
+
function detectSubprocessEdges(files, projectRoot) {
|
|
3194
|
+
const edges = [];
|
|
3195
|
+
const knownFiles = new Set(files.map((f) => f.filePath));
|
|
3196
|
+
const basenameMap = /* @__PURE__ */ new Map();
|
|
3197
|
+
for (const f of files) {
|
|
3198
|
+
const base = basename(f.filePath);
|
|
3199
|
+
if (!basenameMap.has(base)) basenameMap.set(base, []);
|
|
3200
|
+
basenameMap.get(base).push(f.filePath);
|
|
3201
|
+
}
|
|
3202
|
+
for (const file of files) {
|
|
3203
|
+
const fullPath = join10(projectRoot, file.filePath);
|
|
3204
|
+
if (!resolve5(fullPath).startsWith(resolve5(projectRoot))) continue;
|
|
3205
|
+
let source;
|
|
3206
|
+
try {
|
|
3207
|
+
source = readFileSync7(fullPath, "utf-8");
|
|
3208
|
+
} catch {
|
|
3209
|
+
continue;
|
|
3210
|
+
}
|
|
3211
|
+
const calls = extractSubprocessCalls(source, file.filePath);
|
|
3212
|
+
for (const call of calls) {
|
|
3213
|
+
let targetFile = null;
|
|
3214
|
+
let confidence = "high";
|
|
3215
|
+
if (knownFiles.has(call.calledFile)) {
|
|
3216
|
+
targetFile = call.calledFile;
|
|
3217
|
+
confidence = "high";
|
|
3218
|
+
} else {
|
|
3219
|
+
const base = basename(call.calledFile);
|
|
3220
|
+
const candidates = basenameMap.get(base);
|
|
3221
|
+
if (candidates && candidates.length > 0) {
|
|
3222
|
+
const exactCandidate = candidates.find((c) => c.endsWith(call.calledFile));
|
|
3223
|
+
if (exactCandidate) {
|
|
3224
|
+
targetFile = exactCandidate;
|
|
3225
|
+
confidence = "high";
|
|
3226
|
+
} else {
|
|
3227
|
+
targetFile = candidates[0];
|
|
3228
|
+
confidence = "medium";
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
if (!targetFile) continue;
|
|
3233
|
+
if (targetFile === call.file) continue;
|
|
3234
|
+
edges.push({
|
|
3235
|
+
sourceFile: call.file,
|
|
3236
|
+
targetFile,
|
|
3237
|
+
edgeType: "subprocess",
|
|
3238
|
+
confidence,
|
|
3239
|
+
sourceLanguage: getLanguage2(call.file),
|
|
3240
|
+
targetLanguage: getLanguage2(targetFile),
|
|
3241
|
+
sourceLine: call.line,
|
|
3242
|
+
metadata: {
|
|
3243
|
+
command: call.command,
|
|
3244
|
+
calledFile: call.calledFile
|
|
3245
|
+
}
|
|
3246
|
+
});
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
return edges;
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
// src/cross-language/index.ts
|
|
3253
|
+
function detectCrossLanguageEdges(files, projectRoot, graph) {
|
|
3254
|
+
const startTime = Date.now();
|
|
3255
|
+
const restApiEdges = detectRestApiEdges(files, projectRoot);
|
|
3256
|
+
const subprocessEdges = detectSubprocessEdges(files, projectRoot);
|
|
3257
|
+
const allEdges = [...restApiEdges, ...subprocessEdges];
|
|
3258
|
+
for (const edge of allEdges) {
|
|
3259
|
+
const sourceNodeId = `${edge.sourceFile}::__file__`;
|
|
3260
|
+
const targetNodeId = `${edge.targetFile}::__file__`;
|
|
3261
|
+
if (!graph.hasNode(sourceNodeId)) {
|
|
3262
|
+
let hasSourceFile = false;
|
|
3263
|
+
graph.forEachNode((_nodeId, attrs) => {
|
|
3264
|
+
if (attrs.filePath === edge.sourceFile) hasSourceFile = true;
|
|
3265
|
+
});
|
|
3266
|
+
if (!hasSourceFile) continue;
|
|
3267
|
+
graph.addNode(sourceNodeId, {
|
|
3268
|
+
name: "__file__",
|
|
3269
|
+
kind: "import",
|
|
3270
|
+
filePath: edge.sourceFile,
|
|
3271
|
+
startLine: 1,
|
|
3272
|
+
endLine: 1,
|
|
3273
|
+
exported: false
|
|
3274
|
+
});
|
|
3275
|
+
}
|
|
3276
|
+
if (!graph.hasNode(targetNodeId)) {
|
|
3277
|
+
let hasTargetFile = false;
|
|
3278
|
+
graph.forEachNode((_nodeId, attrs) => {
|
|
3279
|
+
if (attrs.filePath === edge.targetFile) hasTargetFile = true;
|
|
3280
|
+
});
|
|
3281
|
+
if (!hasTargetFile) continue;
|
|
3282
|
+
graph.addNode(targetNodeId, {
|
|
3283
|
+
name: "__file__",
|
|
3284
|
+
kind: "import",
|
|
3285
|
+
filePath: edge.targetFile,
|
|
3286
|
+
startLine: 1,
|
|
3287
|
+
endLine: 1,
|
|
3288
|
+
exported: false
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
graph.mergeEdge(sourceNodeId, targetNodeId, {
|
|
3292
|
+
kind: edge.edgeType,
|
|
3293
|
+
filePath: edge.sourceFile,
|
|
3294
|
+
line: edge.sourceLine || 1,
|
|
3295
|
+
crossLanguage: true,
|
|
3296
|
+
confidence: edge.confidence,
|
|
3297
|
+
edgeType: edge.edgeType,
|
|
3298
|
+
httpMethod: edge.metadata.httpMethod,
|
|
3299
|
+
path: edge.metadata.path,
|
|
3300
|
+
command: edge.metadata.command,
|
|
3301
|
+
calledFile: edge.metadata.calledFile
|
|
3302
|
+
});
|
|
3303
|
+
}
|
|
3304
|
+
const detectionTimeMs = Date.now() - startTime;
|
|
3305
|
+
return {
|
|
3306
|
+
edges: allEdges,
|
|
3307
|
+
stats: {
|
|
3308
|
+
restApiEdges: restApiEdges.length,
|
|
3309
|
+
subprocessEdges: subprocessEdges.length,
|
|
3310
|
+
filesAnalyzed: files.length,
|
|
3311
|
+
detectionTimeMs
|
|
3312
|
+
}
|
|
3313
|
+
};
|
|
3314
|
+
}
|
|
3315
|
+
|
|
2872
3316
|
// src/graph/index.ts
|
|
2873
3317
|
import { DirectedGraph } from "graphology";
|
|
2874
|
-
function buildGraph(parsedFiles) {
|
|
3318
|
+
function buildGraph(parsedFiles, projectRoot) {
|
|
2875
3319
|
const graph = new DirectedGraph();
|
|
2876
3320
|
for (const file of parsedFiles) {
|
|
2877
3321
|
for (const symbol of file.symbols) {
|
|
@@ -2928,6 +3372,12 @@ function buildGraph(parsedFiles) {
|
|
|
2928
3372
|
}
|
|
2929
3373
|
}
|
|
2930
3374
|
}
|
|
3375
|
+
if (projectRoot) {
|
|
3376
|
+
const result = detectCrossLanguageEdges(parsedFiles, projectRoot, graph);
|
|
3377
|
+
if (result.stats.restApiEdges > 0 || result.stats.subprocessEdges > 0) {
|
|
3378
|
+
console.error(`Cross-language edges: ${result.stats.restApiEdges} rest-api, ${result.stats.subprocessEdges} subprocess detected`);
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
2931
3381
|
return graph;
|
|
2932
3382
|
}
|
|
2933
3383
|
|
|
@@ -3059,7 +3509,9 @@ function getCrossFileEdges(graph) {
|
|
|
3059
3509
|
target,
|
|
3060
3510
|
sourceFile: sourceAttrs.filePath,
|
|
3061
3511
|
targetFile: targetAttrs.filePath,
|
|
3062
|
-
kind: attrs.kind
|
|
3512
|
+
kind: attrs.kind,
|
|
3513
|
+
crossLanguage: attrs.crossLanguage || false,
|
|
3514
|
+
edgeType: attrs.edgeType
|
|
3063
3515
|
});
|
|
3064
3516
|
}
|
|
3065
3517
|
});
|
|
@@ -3507,8 +3959,8 @@ function calculateDepthScore(graph) {
|
|
|
3507
3959
|
}
|
|
3508
3960
|
|
|
3509
3961
|
// src/health/index.ts
|
|
3510
|
-
import { readFileSync as
|
|
3511
|
-
import {
|
|
3962
|
+
import { readFileSync as readFileSync8, writeFileSync, existsSync as existsSync8, mkdirSync } from "fs";
|
|
3963
|
+
import { dirname as dirname8, resolve as resolve6 } from "path";
|
|
3512
3964
|
function calculateHealthScore(graph, projectRoot) {
|
|
3513
3965
|
const coupling = calculateCouplingScore(graph);
|
|
3514
3966
|
const cohesion = calculateCohesionScore(graph);
|
|
@@ -3607,7 +4059,11 @@ function getHealthTrend(projectRoot, currentScore) {
|
|
|
3607
4059
|
}
|
|
3608
4060
|
}
|
|
3609
4061
|
function saveHealthHistory(projectRoot, report) {
|
|
3610
|
-
const
|
|
4062
|
+
const resolvedRoot = resolve6(projectRoot);
|
|
4063
|
+
const historyFile = resolve6(resolvedRoot, ".depwire", "health-history.json");
|
|
4064
|
+
if (!historyFile.startsWith(resolvedRoot)) {
|
|
4065
|
+
return;
|
|
4066
|
+
}
|
|
3611
4067
|
const entry = {
|
|
3612
4068
|
timestamp: report.timestamp,
|
|
3613
4069
|
score: report.overall,
|
|
@@ -3621,7 +4077,8 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
3621
4077
|
let history = [];
|
|
3622
4078
|
if (existsSync8(historyFile)) {
|
|
3623
4079
|
try {
|
|
3624
|
-
|
|
4080
|
+
if (!historyFile.startsWith(resolvedRoot)) return;
|
|
4081
|
+
const content = readFileSync8(historyFile, "utf-8");
|
|
3625
4082
|
history = JSON.parse(content);
|
|
3626
4083
|
} catch {
|
|
3627
4084
|
}
|
|
@@ -3631,15 +4088,18 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
3631
4088
|
history = history.slice(-50);
|
|
3632
4089
|
}
|
|
3633
4090
|
mkdirSync(dirname8(historyFile), { recursive: true });
|
|
4091
|
+
if (!historyFile.startsWith(resolvedRoot)) return;
|
|
3634
4092
|
writeFileSync(historyFile, JSON.stringify(history, null, 2), "utf-8");
|
|
3635
4093
|
}
|
|
3636
4094
|
function loadHealthHistory(projectRoot) {
|
|
3637
|
-
const
|
|
3638
|
-
|
|
4095
|
+
const resolvedRoot = resolve6(projectRoot);
|
|
4096
|
+
const historyFile = resolve6(resolvedRoot, ".depwire", "health-history.json");
|
|
4097
|
+
if (!historyFile.startsWith(resolvedRoot) || !existsSync8(historyFile)) {
|
|
3639
4098
|
return [];
|
|
3640
4099
|
}
|
|
3641
4100
|
try {
|
|
3642
|
-
|
|
4101
|
+
if (!historyFile.startsWith(resolvedRoot)) return [];
|
|
4102
|
+
const content = readFileSync8(historyFile, "utf-8");
|
|
3643
4103
|
return JSON.parse(content);
|
|
3644
4104
|
} catch {
|
|
3645
4105
|
return [];
|
|
@@ -3648,7 +4108,7 @@ function loadHealthHistory(projectRoot) {
|
|
|
3648
4108
|
|
|
3649
4109
|
// src/dead-code/detector.ts
|
|
3650
4110
|
import path2 from "path";
|
|
3651
|
-
import { readFileSync as
|
|
4111
|
+
import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
3652
4112
|
function findDeadSymbols(graph, projectRoot, includeTests = false, debug = false) {
|
|
3653
4113
|
const deadSymbols = [];
|
|
3654
4114
|
const context = { graph, projectRoot };
|
|
@@ -3777,12 +4237,13 @@ function isRelevantForDeadCodeDetection(attrs) {
|
|
|
3777
4237
|
}
|
|
3778
4238
|
function getPackageEntryPoints(projectRoot) {
|
|
3779
4239
|
const entryPoints = /* @__PURE__ */ new Set();
|
|
3780
|
-
const
|
|
3781
|
-
|
|
4240
|
+
const resolvedRoot = path2.resolve(projectRoot);
|
|
4241
|
+
const packageJsonPath = path2.resolve(resolvedRoot, "package.json");
|
|
4242
|
+
if (!packageJsonPath.startsWith(resolvedRoot) || !existsSync9(packageJsonPath)) {
|
|
3782
4243
|
return entryPoints;
|
|
3783
4244
|
}
|
|
3784
4245
|
try {
|
|
3785
|
-
const packageJson = JSON.parse(
|
|
4246
|
+
const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
|
|
3786
4247
|
if (packageJson.main) {
|
|
3787
4248
|
entryPoints.add(path2.resolve(projectRoot, packageJson.main));
|
|
3788
4249
|
}
|
|
@@ -3923,8 +4384,8 @@ function generateReason(symbol, confidence) {
|
|
|
3923
4384
|
return "Potentially unused";
|
|
3924
4385
|
}
|
|
3925
4386
|
function isBarrelFile(filePath) {
|
|
3926
|
-
const
|
|
3927
|
-
return
|
|
4387
|
+
const basename5 = path3.basename(filePath);
|
|
4388
|
+
return basename5 === "index.ts" || basename5 === "index.js";
|
|
3928
4389
|
}
|
|
3929
4390
|
function isTestFile2(filePath) {
|
|
3930
4391
|
return filePath.includes("__tests__/") || filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("/test/") || filePath.includes("/tests/");
|
|
@@ -4104,7 +4565,7 @@ function filterByConfidence(symbols, minConfidence) {
|
|
|
4104
4565
|
|
|
4105
4566
|
// src/docs/generator.ts
|
|
4106
4567
|
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync12 } from "fs";
|
|
4107
|
-
import { join as
|
|
4568
|
+
import { join as join14 } from "path";
|
|
4108
4569
|
|
|
4109
4570
|
// src/docs/architecture.ts
|
|
4110
4571
|
import { dirname as dirname9 } from "path";
|
|
@@ -4506,7 +4967,7 @@ function detectCycles(graph) {
|
|
|
4506
4967
|
}
|
|
4507
4968
|
|
|
4508
4969
|
// src/docs/conventions.ts
|
|
4509
|
-
import { basename, extname as extname4 } from "path";
|
|
4970
|
+
import { basename as basename2, extname as extname4 } from "path";
|
|
4510
4971
|
function generateConventions(graph, projectRoot, version) {
|
|
4511
4972
|
let output = "";
|
|
4512
4973
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -4544,7 +5005,7 @@ function generateFileOrganization(graph) {
|
|
|
4544
5005
|
graph.forEachNode((node, attrs) => {
|
|
4545
5006
|
if (!files.has(attrs.filePath)) {
|
|
4546
5007
|
files.add(attrs.filePath);
|
|
4547
|
-
const fileName =
|
|
5008
|
+
const fileName = basename2(attrs.filePath);
|
|
4548
5009
|
if (fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx") {
|
|
4549
5010
|
barrelFileCount++;
|
|
4550
5011
|
}
|
|
@@ -4603,7 +5064,7 @@ function generateNamingPatterns(graph) {
|
|
|
4603
5064
|
graph.forEachNode((node, attrs) => {
|
|
4604
5065
|
if (!files.has(attrs.filePath)) {
|
|
4605
5066
|
files.add(attrs.filePath);
|
|
4606
|
-
const fileName =
|
|
5067
|
+
const fileName = basename2(attrs.filePath, extname4(attrs.filePath));
|
|
4607
5068
|
if (isCamelCase(fileName)) patterns.files.camelCase++;
|
|
4608
5069
|
else if (isPascalCase(fileName)) patterns.files.PascalCase++;
|
|
4609
5070
|
else if (isKebabCase(fileName)) patterns.files.kebabCase++;
|
|
@@ -5668,7 +6129,7 @@ function generateDepwireUsage(projectRoot) {
|
|
|
5668
6129
|
}
|
|
5669
6130
|
|
|
5670
6131
|
// src/docs/files.ts
|
|
5671
|
-
import { dirname as dirname11, basename as
|
|
6132
|
+
import { dirname as dirname11, basename as basename3 } from "path";
|
|
5672
6133
|
function generateFiles(graph, projectRoot, version) {
|
|
5673
6134
|
let output = "";
|
|
5674
6135
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -5787,7 +6248,7 @@ function generateDirectoryBreakdown(graph) {
|
|
|
5787
6248
|
dirStats.symbolCount += file.symbolCount;
|
|
5788
6249
|
if (file.totalConnections > dirStats.maxConnections) {
|
|
5789
6250
|
dirStats.maxConnections = file.totalConnections;
|
|
5790
|
-
dirStats.mostConnectedFile =
|
|
6251
|
+
dirStats.mostConnectedFile = basename3(file.filePath);
|
|
5791
6252
|
}
|
|
5792
6253
|
}
|
|
5793
6254
|
if (dirMap.size === 0) {
|
|
@@ -6415,7 +6876,7 @@ function generateRecommendations(graph) {
|
|
|
6415
6876
|
}
|
|
6416
6877
|
|
|
6417
6878
|
// src/docs/tests.ts
|
|
6418
|
-
import { basename as
|
|
6879
|
+
import { basename as basename4, dirname as dirname12 } from "path";
|
|
6419
6880
|
function generateTests(graph, projectRoot, version) {
|
|
6420
6881
|
let output = "";
|
|
6421
6882
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -6443,7 +6904,7 @@ function getFileCount8(graph) {
|
|
|
6443
6904
|
return files.size;
|
|
6444
6905
|
}
|
|
6445
6906
|
function isTestFile3(filePath) {
|
|
6446
|
-
const fileName =
|
|
6907
|
+
const fileName = basename4(filePath).toLowerCase();
|
|
6447
6908
|
const dirPath = dirname12(filePath).toLowerCase();
|
|
6448
6909
|
if (dirPath.includes("test") || dirPath.includes("spec") || dirPath.includes("__tests__")) {
|
|
6449
6910
|
return true;
|
|
@@ -6502,7 +6963,7 @@ function generateTestFileInventory(graph) {
|
|
|
6502
6963
|
return output;
|
|
6503
6964
|
}
|
|
6504
6965
|
function matchTestToSource(testFile) {
|
|
6505
|
-
const testFileName =
|
|
6966
|
+
const testFileName = basename4(testFile);
|
|
6506
6967
|
const testDir = dirname12(testFile);
|
|
6507
6968
|
let sourceFileName = testFileName.replace(/\.test\./g, ".").replace(/\.spec\./g, ".").replace(/_test\./g, ".").replace(/_spec\./g, ".");
|
|
6508
6969
|
const possiblePaths = [];
|
|
@@ -6664,7 +7125,7 @@ function generateTestCoverageMap(graph) {
|
|
|
6664
7125
|
const rows = mappings.slice(0, 30).map((m) => [
|
|
6665
7126
|
`\`${m.sourceFile}\``,
|
|
6666
7127
|
m.hasTest ? "\u2705" : "\u274C",
|
|
6667
|
-
m.testFile ? `\`${
|
|
7128
|
+
m.testFile ? `\`${basename4(m.testFile)}\`` : "-",
|
|
6668
7129
|
formatNumber(m.symbolCount)
|
|
6669
7130
|
]);
|
|
6670
7131
|
let output = table(headers, rows);
|
|
@@ -7440,8 +7901,8 @@ function getTopLevelDir2(filePath) {
|
|
|
7440
7901
|
}
|
|
7441
7902
|
|
|
7442
7903
|
// src/docs/status.ts
|
|
7443
|
-
import { readFileSync as
|
|
7444
|
-
import {
|
|
7904
|
+
import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
|
|
7905
|
+
import { resolve as resolve7 } from "path";
|
|
7445
7906
|
function generateStatus(graph, projectRoot, version) {
|
|
7446
7907
|
let output = "";
|
|
7447
7908
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -7474,12 +7935,16 @@ function getFileCount11(graph) {
|
|
|
7474
7935
|
}
|
|
7475
7936
|
function extractComments(projectRoot, filePath) {
|
|
7476
7937
|
const comments = [];
|
|
7477
|
-
const
|
|
7938
|
+
const resolvedRoot = resolve7(projectRoot);
|
|
7939
|
+
const fullPath = resolve7(resolvedRoot, filePath);
|
|
7940
|
+
if (!fullPath.startsWith(resolvedRoot)) {
|
|
7941
|
+
return comments;
|
|
7942
|
+
}
|
|
7478
7943
|
if (!existsSync10(fullPath)) {
|
|
7479
7944
|
return comments;
|
|
7480
7945
|
}
|
|
7481
7946
|
try {
|
|
7482
|
-
const content =
|
|
7947
|
+
const content = readFileSync10(fullPath, "utf-8");
|
|
7483
7948
|
const lines = content.split("\n");
|
|
7484
7949
|
const patterns = [
|
|
7485
7950
|
{ type: "TODO", regex: /(?:\/\/|#|\/\*)\s*TODO:?\s*(.+)/i },
|
|
@@ -8058,15 +8523,16 @@ function generateConfidenceSection(title, description, symbols, projectRoot) {
|
|
|
8058
8523
|
}
|
|
8059
8524
|
|
|
8060
8525
|
// src/docs/metadata.ts
|
|
8061
|
-
import { existsSync as existsSync11, readFileSync as
|
|
8062
|
-
import {
|
|
8526
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync2 } from "fs";
|
|
8527
|
+
import { resolve as resolve8 } from "path";
|
|
8063
8528
|
function loadMetadata(outputDir) {
|
|
8064
|
-
const
|
|
8065
|
-
|
|
8529
|
+
const resolvedDir = resolve8(outputDir);
|
|
8530
|
+
const metadataPath = resolve8(resolvedDir, "metadata.json");
|
|
8531
|
+
if (!metadataPath.startsWith(resolvedDir) || !existsSync11(metadataPath)) {
|
|
8066
8532
|
return null;
|
|
8067
8533
|
}
|
|
8068
8534
|
try {
|
|
8069
|
-
const content =
|
|
8535
|
+
const content = readFileSync11(metadataPath, "utf-8");
|
|
8070
8536
|
return JSON.parse(content);
|
|
8071
8537
|
} catch (err) {
|
|
8072
8538
|
console.error("Failed to load metadata:", err);
|
|
@@ -8074,7 +8540,11 @@ function loadMetadata(outputDir) {
|
|
|
8074
8540
|
}
|
|
8075
8541
|
}
|
|
8076
8542
|
function saveMetadata(outputDir, metadata) {
|
|
8077
|
-
const
|
|
8543
|
+
const resolvedDir = resolve8(outputDir);
|
|
8544
|
+
const metadataPath = resolve8(resolvedDir, "metadata.json");
|
|
8545
|
+
if (!metadataPath.startsWith(resolvedDir)) {
|
|
8546
|
+
throw new Error(`Path traversal attempt blocked: ${metadataPath}`);
|
|
8547
|
+
}
|
|
8078
8548
|
writeFileSync2(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
8079
8549
|
}
|
|
8080
8550
|
function createMetadata(version, projectPath, fileCount, symbolCount, edgeCount, docTypes) {
|
|
@@ -8156,7 +8626,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8156
8626
|
try {
|
|
8157
8627
|
if (options.verbose) console.log("Generating ARCHITECTURE.md...");
|
|
8158
8628
|
const content = generateArchitecture(graph, projectRoot, version, parseTime);
|
|
8159
|
-
const filePath =
|
|
8629
|
+
const filePath = join14(options.outputDir, "ARCHITECTURE.md");
|
|
8160
8630
|
writeFileSync3(filePath, content, "utf-8");
|
|
8161
8631
|
generated.push("ARCHITECTURE.md");
|
|
8162
8632
|
} catch (err) {
|
|
@@ -8167,7 +8637,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8167
8637
|
try {
|
|
8168
8638
|
if (options.verbose) console.log("Generating CONVENTIONS.md...");
|
|
8169
8639
|
const content = generateConventions(graph, projectRoot, version);
|
|
8170
|
-
const filePath =
|
|
8640
|
+
const filePath = join14(options.outputDir, "CONVENTIONS.md");
|
|
8171
8641
|
writeFileSync3(filePath, content, "utf-8");
|
|
8172
8642
|
generated.push("CONVENTIONS.md");
|
|
8173
8643
|
} catch (err) {
|
|
@@ -8178,7 +8648,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8178
8648
|
try {
|
|
8179
8649
|
if (options.verbose) console.log("Generating DEPENDENCIES.md...");
|
|
8180
8650
|
const content = generateDependencies(graph, projectRoot, version);
|
|
8181
|
-
const filePath =
|
|
8651
|
+
const filePath = join14(options.outputDir, "DEPENDENCIES.md");
|
|
8182
8652
|
writeFileSync3(filePath, content, "utf-8");
|
|
8183
8653
|
generated.push("DEPENDENCIES.md");
|
|
8184
8654
|
} catch (err) {
|
|
@@ -8189,7 +8659,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8189
8659
|
try {
|
|
8190
8660
|
if (options.verbose) console.log("Generating ONBOARDING.md...");
|
|
8191
8661
|
const content = generateOnboarding(graph, projectRoot, version);
|
|
8192
|
-
const filePath =
|
|
8662
|
+
const filePath = join14(options.outputDir, "ONBOARDING.md");
|
|
8193
8663
|
writeFileSync3(filePath, content, "utf-8");
|
|
8194
8664
|
generated.push("ONBOARDING.md");
|
|
8195
8665
|
} catch (err) {
|
|
@@ -8200,7 +8670,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8200
8670
|
try {
|
|
8201
8671
|
if (options.verbose) console.log("Generating FILES.md...");
|
|
8202
8672
|
const content = generateFiles(graph, projectRoot, version);
|
|
8203
|
-
const filePath =
|
|
8673
|
+
const filePath = join14(options.outputDir, "FILES.md");
|
|
8204
8674
|
writeFileSync3(filePath, content, "utf-8");
|
|
8205
8675
|
generated.push("FILES.md");
|
|
8206
8676
|
} catch (err) {
|
|
@@ -8211,7 +8681,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8211
8681
|
try {
|
|
8212
8682
|
if (options.verbose) console.log("Generating API_SURFACE.md...");
|
|
8213
8683
|
const content = generateApiSurface(graph, projectRoot, version);
|
|
8214
|
-
const filePath =
|
|
8684
|
+
const filePath = join14(options.outputDir, "API_SURFACE.md");
|
|
8215
8685
|
writeFileSync3(filePath, content, "utf-8");
|
|
8216
8686
|
generated.push("API_SURFACE.md");
|
|
8217
8687
|
} catch (err) {
|
|
@@ -8222,7 +8692,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8222
8692
|
try {
|
|
8223
8693
|
if (options.verbose) console.log("Generating ERRORS.md...");
|
|
8224
8694
|
const content = generateErrors(graph, projectRoot, version);
|
|
8225
|
-
const filePath =
|
|
8695
|
+
const filePath = join14(options.outputDir, "ERRORS.md");
|
|
8226
8696
|
writeFileSync3(filePath, content, "utf-8");
|
|
8227
8697
|
generated.push("ERRORS.md");
|
|
8228
8698
|
} catch (err) {
|
|
@@ -8233,7 +8703,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8233
8703
|
try {
|
|
8234
8704
|
if (options.verbose) console.log("Generating TESTS.md...");
|
|
8235
8705
|
const content = generateTests(graph, projectRoot, version);
|
|
8236
|
-
const filePath =
|
|
8706
|
+
const filePath = join14(options.outputDir, "TESTS.md");
|
|
8237
8707
|
writeFileSync3(filePath, content, "utf-8");
|
|
8238
8708
|
generated.push("TESTS.md");
|
|
8239
8709
|
} catch (err) {
|
|
@@ -8244,7 +8714,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8244
8714
|
try {
|
|
8245
8715
|
if (options.verbose) console.log("Generating HISTORY.md...");
|
|
8246
8716
|
const content = generateHistory(graph, projectRoot, version);
|
|
8247
|
-
const filePath =
|
|
8717
|
+
const filePath = join14(options.outputDir, "HISTORY.md");
|
|
8248
8718
|
writeFileSync3(filePath, content, "utf-8");
|
|
8249
8719
|
generated.push("HISTORY.md");
|
|
8250
8720
|
} catch (err) {
|
|
@@ -8255,7 +8725,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8255
8725
|
try {
|
|
8256
8726
|
if (options.verbose) console.log("Generating CURRENT.md...");
|
|
8257
8727
|
const content = generateCurrent(graph, projectRoot, version);
|
|
8258
|
-
const filePath =
|
|
8728
|
+
const filePath = join14(options.outputDir, "CURRENT.md");
|
|
8259
8729
|
writeFileSync3(filePath, content, "utf-8");
|
|
8260
8730
|
generated.push("CURRENT.md");
|
|
8261
8731
|
} catch (err) {
|
|
@@ -8266,7 +8736,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8266
8736
|
try {
|
|
8267
8737
|
if (options.verbose) console.log("Generating STATUS.md...");
|
|
8268
8738
|
const content = generateStatus(graph, projectRoot, version);
|
|
8269
|
-
const filePath =
|
|
8739
|
+
const filePath = join14(options.outputDir, "STATUS.md");
|
|
8270
8740
|
writeFileSync3(filePath, content, "utf-8");
|
|
8271
8741
|
generated.push("STATUS.md");
|
|
8272
8742
|
} catch (err) {
|
|
@@ -8277,7 +8747,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8277
8747
|
try {
|
|
8278
8748
|
if (options.verbose) console.log("Generating HEALTH.md...");
|
|
8279
8749
|
const content = generateHealth(graph, projectRoot, version);
|
|
8280
|
-
const filePath =
|
|
8750
|
+
const filePath = join14(options.outputDir, "HEALTH.md");
|
|
8281
8751
|
writeFileSync3(filePath, content, "utf-8");
|
|
8282
8752
|
generated.push("HEALTH.md");
|
|
8283
8753
|
} catch (err) {
|
|
@@ -8288,7 +8758,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
8288
8758
|
try {
|
|
8289
8759
|
if (options.verbose) console.log("Generating DEAD_CODE.md...");
|
|
8290
8760
|
const content = generateDeadCode(graph, projectRoot, version);
|
|
8291
|
-
const filePath =
|
|
8761
|
+
const filePath = join14(options.outputDir, "DEAD_CODE.md");
|
|
8292
8762
|
writeFileSync3(filePath, content, "utf-8");
|
|
8293
8763
|
generated.push("DEAD_CODE.md");
|
|
8294
8764
|
} catch (err) {
|
|
@@ -8332,13 +8802,13 @@ function getFileCount13(graph) {
|
|
|
8332
8802
|
}
|
|
8333
8803
|
|
|
8334
8804
|
// src/simulation/engine.ts
|
|
8335
|
-
import { dirname as dirname15, join as
|
|
8336
|
-
function
|
|
8805
|
+
import { dirname as dirname15, join as join15 } from "path";
|
|
8806
|
+
function normalizePath2(p) {
|
|
8337
8807
|
return p.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
8338
8808
|
}
|
|
8339
8809
|
function fileMatch(nodeFilePath, target) {
|
|
8340
|
-
const a =
|
|
8341
|
-
const b =
|
|
8810
|
+
const a = normalizePath2(nodeFilePath);
|
|
8811
|
+
const b = normalizePath2(target);
|
|
8342
8812
|
return a === b || a.endsWith("/" + b) || b.endsWith("/" + a);
|
|
8343
8813
|
}
|
|
8344
8814
|
var SimulationEngine = class {
|
|
@@ -8403,8 +8873,8 @@ var SimulationEngine = class {
|
|
|
8403
8873
|
}
|
|
8404
8874
|
// ── Action implementations ─────────────────────────────────────
|
|
8405
8875
|
applyMove(clone, target, destination, brokenImports) {
|
|
8406
|
-
const normalizedTarget =
|
|
8407
|
-
const normalizedDest =
|
|
8876
|
+
const normalizedTarget = normalizePath2(target);
|
|
8877
|
+
const normalizedDest = normalizePath2(destination);
|
|
8408
8878
|
const nodesToMove = clone.filterNodes(
|
|
8409
8879
|
(_node, attrs) => fileMatch(attrs.filePath, target)
|
|
8410
8880
|
);
|
|
@@ -8463,11 +8933,11 @@ var SimulationEngine = class {
|
|
|
8463
8933
|
}
|
|
8464
8934
|
}
|
|
8465
8935
|
applyRename(clone, target, newName, brokenImports) {
|
|
8466
|
-
const destination =
|
|
8936
|
+
const destination = join15(dirname15(target), newName);
|
|
8467
8937
|
this.applyMove(clone, target, destination, brokenImports);
|
|
8468
8938
|
}
|
|
8469
8939
|
applySplit(clone, target, newFile, symbols, brokenImports) {
|
|
8470
|
-
const normalizedNewFile =
|
|
8940
|
+
const normalizedNewFile = normalizePath2(newFile);
|
|
8471
8941
|
const nodesToSplit = clone.filterNodes((_node, attrs) => {
|
|
8472
8942
|
return fileMatch(attrs.filePath, target) && symbols.includes(attrs.name);
|
|
8473
8943
|
});
|
|
@@ -8503,7 +8973,7 @@ var SimulationEngine = class {
|
|
|
8503
8973
|
}
|
|
8504
8974
|
}
|
|
8505
8975
|
applyMerge(clone, target, source, brokenImports) {
|
|
8506
|
-
const normalizedTarget =
|
|
8976
|
+
const normalizedTarget = normalizePath2(target);
|
|
8507
8977
|
const sourceNodes = clone.filterNodes(
|
|
8508
8978
|
(_node, attrs) => fileMatch(attrs.filePath, source)
|
|
8509
8979
|
);
|
|
@@ -8664,12 +9134,12 @@ var SimulationEngine = class {
|
|
|
8664
9134
|
|
|
8665
9135
|
// src/security/scanner.ts
|
|
8666
9136
|
import { existsSync as existsSync14 } from "fs";
|
|
8667
|
-
import { join as
|
|
9137
|
+
import { join as join25 } from "path";
|
|
8668
9138
|
|
|
8669
9139
|
// src/security/checks/dependencies.ts
|
|
8670
9140
|
import { execSync as execSync2 } from "child_process";
|
|
8671
|
-
import { existsSync as existsSync13, readFileSync as
|
|
8672
|
-
import { join as
|
|
9141
|
+
import { existsSync as existsSync13, readFileSync as readFileSync12, readdirSync as readdirSync5 } from "fs";
|
|
9142
|
+
import { join as join16 } from "path";
|
|
8673
9143
|
function cvssToSeverity(score) {
|
|
8674
9144
|
if (score >= 9) return "critical";
|
|
8675
9145
|
if (score >= 7) return "high";
|
|
@@ -8679,18 +9149,18 @@ function cvssToSeverity(score) {
|
|
|
8679
9149
|
async function checkDependencies(_files, projectRoot) {
|
|
8680
9150
|
const findings = [];
|
|
8681
9151
|
try {
|
|
8682
|
-
if (existsSync13(
|
|
9152
|
+
if (existsSync13(join16(projectRoot, "package.json"))) {
|
|
8683
9153
|
findings.push(...checkNpmAudit(projectRoot));
|
|
8684
9154
|
findings.push(...checkPackageJsonPatterns(projectRoot));
|
|
8685
9155
|
findings.push(...checkPostinstallScripts(projectRoot));
|
|
8686
9156
|
}
|
|
8687
|
-
if (existsSync13(
|
|
9157
|
+
if (existsSync13(join16(projectRoot, "requirements.txt")) || existsSync13(join16(projectRoot, "pyproject.toml"))) {
|
|
8688
9158
|
findings.push(...checkPipAudit(projectRoot));
|
|
8689
9159
|
}
|
|
8690
|
-
if (existsSync13(
|
|
9160
|
+
if (existsSync13(join16(projectRoot, "Cargo.toml"))) {
|
|
8691
9161
|
findings.push(...checkCargoAudit(projectRoot));
|
|
8692
9162
|
}
|
|
8693
|
-
if (existsSync13(
|
|
9163
|
+
if (existsSync13(join16(projectRoot, "go.mod"))) {
|
|
8694
9164
|
findings.push(...checkGoVerify(projectRoot));
|
|
8695
9165
|
}
|
|
8696
9166
|
} catch (err) {
|
|
@@ -8779,8 +9249,8 @@ function checkNpmAudit(projectRoot) {
|
|
|
8779
9249
|
function checkPackageJsonPatterns(projectRoot) {
|
|
8780
9250
|
const findings = [];
|
|
8781
9251
|
try {
|
|
8782
|
-
const pkgPath =
|
|
8783
|
-
const pkg = JSON.parse(
|
|
9252
|
+
const pkgPath = join16(projectRoot, "package.json");
|
|
9253
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
8784
9254
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
8785
9255
|
for (const [name, version] of Object.entries(allDeps)) {
|
|
8786
9256
|
if (version.startsWith("^") || version.startsWith("~")) {
|
|
@@ -8802,15 +9272,15 @@ function checkPackageJsonPatterns(projectRoot) {
|
|
|
8802
9272
|
}
|
|
8803
9273
|
function checkPostinstallScripts(projectRoot) {
|
|
8804
9274
|
const findings = [];
|
|
8805
|
-
const nodeModules =
|
|
9275
|
+
const nodeModules = join16(projectRoot, "node_modules");
|
|
8806
9276
|
if (!existsSync13(nodeModules)) return findings;
|
|
8807
9277
|
try {
|
|
8808
9278
|
const topLevelDeps = readdirSync5(nodeModules).filter((d) => !d.startsWith("."));
|
|
8809
9279
|
for (const dep of topLevelDeps) {
|
|
8810
|
-
const depPkgPath =
|
|
9280
|
+
const depPkgPath = join16(nodeModules, dep, "package.json");
|
|
8811
9281
|
if (!existsSync13(depPkgPath)) continue;
|
|
8812
9282
|
try {
|
|
8813
|
-
const depPkg = JSON.parse(
|
|
9283
|
+
const depPkg = JSON.parse(readFileSync12(depPkgPath, "utf-8"));
|
|
8814
9284
|
const scripts = depPkg.scripts || {};
|
|
8815
9285
|
if (scripts.postinstall || scripts.preinstall || scripts.install) {
|
|
8816
9286
|
const scriptName = scripts.postinstall ? "postinstall" : scripts.preinstall ? "preinstall" : "install";
|
|
@@ -8848,7 +9318,7 @@ function checkPipAudit(projectRoot) {
|
|
|
8848
9318
|
id: "",
|
|
8849
9319
|
severity: cvssToSeverity(vuln.cvss?.score || 5),
|
|
8850
9320
|
vulnerabilityClass: "dependency-cve",
|
|
8851
|
-
file: existsSync13(
|
|
9321
|
+
file: existsSync13(join16(projectRoot, "requirements.txt")) ? "requirements.txt" : "pyproject.toml",
|
|
8852
9322
|
title: `Vulnerable Python dependency: ${vuln.name}`,
|
|
8853
9323
|
description: `${vuln.name}@${vuln.version} \u2014 ${vuln.id}: ${vuln.description || "Known vulnerability"}`,
|
|
8854
9324
|
attackScenario: `An attacker could exploit the vulnerability in ${vuln.name}.`,
|
|
@@ -8945,8 +9415,8 @@ function checkGoVerify(projectRoot) {
|
|
|
8945
9415
|
}
|
|
8946
9416
|
|
|
8947
9417
|
// src/security/checks/injection.ts
|
|
8948
|
-
import { readFileSync as
|
|
8949
|
-
import { join as
|
|
9418
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
9419
|
+
import { join as join17 } from "path";
|
|
8950
9420
|
var SKIP_DIRS = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
8951
9421
|
var TEST_PATTERNS = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__"];
|
|
8952
9422
|
var USER_INPUT_NAMES = /(?:input|user|name|path|query|branch|hash|cmd|command|req\.|params|body|args|url|dir|file|subdirectory)/i;
|
|
@@ -9047,7 +9517,7 @@ async function checkInjection(files, projectRoot) {
|
|
|
9047
9517
|
if (shouldSkip(file.filePath) || isTestFile4(file.filePath)) continue;
|
|
9048
9518
|
let content;
|
|
9049
9519
|
try {
|
|
9050
|
-
content =
|
|
9520
|
+
content = readFileSync13(join17(projectRoot, file.filePath), "utf-8");
|
|
9051
9521
|
} catch {
|
|
9052
9522
|
continue;
|
|
9053
9523
|
}
|
|
@@ -9057,6 +9527,7 @@ async function checkInjection(files, projectRoot) {
|
|
|
9057
9527
|
if (line.trimStart().startsWith("//") || line.trimStart().startsWith("#") || line.trimStart().startsWith("*")) {
|
|
9058
9528
|
continue;
|
|
9059
9529
|
}
|
|
9530
|
+
if (line.includes("depwire-security-reviewed")) continue;
|
|
9060
9531
|
for (const pattern of PATTERNS) {
|
|
9061
9532
|
if (pattern.regex.test(line)) {
|
|
9062
9533
|
let severity = pattern.baseSeverity;
|
|
@@ -9084,8 +9555,8 @@ async function checkInjection(files, projectRoot) {
|
|
|
9084
9555
|
}
|
|
9085
9556
|
|
|
9086
9557
|
// src/security/checks/secrets.ts
|
|
9087
|
-
import { readFileSync as
|
|
9088
|
-
import { join as
|
|
9558
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
9559
|
+
import { join as join18 } from "path";
|
|
9089
9560
|
var SKIP_DIRS2 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9090
9561
|
var TEST_PATTERNS2 = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__", ".example", ".sample"];
|
|
9091
9562
|
var SECRET_PATTERNS = [
|
|
@@ -9118,7 +9589,7 @@ async function checkSecrets(files, projectRoot) {
|
|
|
9118
9589
|
if (shouldSkip2(file.filePath) || isTestFile5(file.filePath)) continue;
|
|
9119
9590
|
let content;
|
|
9120
9591
|
try {
|
|
9121
|
-
content =
|
|
9592
|
+
content = readFileSync14(join18(projectRoot, file.filePath), "utf-8");
|
|
9122
9593
|
} catch {
|
|
9123
9594
|
continue;
|
|
9124
9595
|
}
|
|
@@ -9151,8 +9622,8 @@ async function checkSecrets(files, projectRoot) {
|
|
|
9151
9622
|
}
|
|
9152
9623
|
|
|
9153
9624
|
// src/security/checks/path-traversal.ts
|
|
9154
|
-
import { readFileSync as
|
|
9155
|
-
import { join as
|
|
9625
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
9626
|
+
import { join as join19 } from "path";
|
|
9156
9627
|
var SKIP_DIRS3 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9157
9628
|
var USER_INPUT_VARS = /(?:req\.|params|query|body|input|path|dir|subdirectory|file|userInput|fileName|filePath)/i;
|
|
9158
9629
|
var PATTERNS2 = [
|
|
@@ -9199,7 +9670,7 @@ async function checkPathTraversal(files, projectRoot) {
|
|
|
9199
9670
|
if (shouldSkip3(file.filePath)) continue;
|
|
9200
9671
|
let content;
|
|
9201
9672
|
try {
|
|
9202
|
-
content =
|
|
9673
|
+
content = readFileSync15(join19(projectRoot, file.filePath), "utf-8");
|
|
9203
9674
|
} catch {
|
|
9204
9675
|
continue;
|
|
9205
9676
|
}
|
|
@@ -9217,8 +9688,8 @@ async function checkPathTraversal(files, projectRoot) {
|
|
|
9217
9688
|
if (SAFE_OUTPUT_PATTERNS.test(context)) continue;
|
|
9218
9689
|
}
|
|
9219
9690
|
if (/__dirname/.test(line) && SAFE_DIRNAME_ARGS.test(line)) continue;
|
|
9220
|
-
const nearbyLines = lines.slice(Math.max(0, i -
|
|
9221
|
-
if (nearbyLines.includes("startsWith") && nearbyLines
|
|
9691
|
+
const nearbyLines = lines.slice(Math.max(0, i - 15), Math.min(lines.length, i + 4)).join("\n");
|
|
9692
|
+
if (nearbyLines.includes("startsWith") && /resolve/.test(nearbyLines)) continue;
|
|
9222
9693
|
const severity = inRouteOrTool ? "high" : "medium";
|
|
9223
9694
|
findings.push({
|
|
9224
9695
|
id: "",
|
|
@@ -9241,8 +9712,8 @@ async function checkPathTraversal(files, projectRoot) {
|
|
|
9241
9712
|
}
|
|
9242
9713
|
|
|
9243
9714
|
// src/security/checks/auth.ts
|
|
9244
|
-
import { readFileSync as
|
|
9245
|
-
import { join as
|
|
9715
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
9716
|
+
import { join as join20 } from "path";
|
|
9246
9717
|
var SKIP_DIRS4 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9247
9718
|
function shouldSkip4(filePath) {
|
|
9248
9719
|
return SKIP_DIRS4.some((d) => filePath.includes(d));
|
|
@@ -9258,7 +9729,7 @@ async function checkAuth(files, projectRoot) {
|
|
|
9258
9729
|
if (shouldSkip4(file.filePath)) continue;
|
|
9259
9730
|
let content;
|
|
9260
9731
|
try {
|
|
9261
|
-
content =
|
|
9732
|
+
content = readFileSync16(join20(projectRoot, file.filePath), "utf-8");
|
|
9262
9733
|
} catch {
|
|
9263
9734
|
continue;
|
|
9264
9735
|
}
|
|
@@ -9349,8 +9820,8 @@ async function checkAuth(files, projectRoot) {
|
|
|
9349
9820
|
}
|
|
9350
9821
|
|
|
9351
9822
|
// src/security/checks/input-validation.ts
|
|
9352
|
-
import { readFileSync as
|
|
9353
|
-
import { join as
|
|
9823
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
9824
|
+
import { join as join21 } from "path";
|
|
9354
9825
|
var SKIP_DIRS5 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9355
9826
|
function shouldSkip5(filePath) {
|
|
9356
9827
|
return SKIP_DIRS5.some((d) => filePath.includes(d));
|
|
@@ -9362,7 +9833,7 @@ async function checkInputValidation(files, projectRoot) {
|
|
|
9362
9833
|
if (shouldSkip5(file.filePath)) continue;
|
|
9363
9834
|
let content;
|
|
9364
9835
|
try {
|
|
9365
|
-
content =
|
|
9836
|
+
content = readFileSync17(join21(projectRoot, file.filePath), "utf-8");
|
|
9366
9837
|
} catch {
|
|
9367
9838
|
continue;
|
|
9368
9839
|
}
|
|
@@ -9439,8 +9910,8 @@ async function checkInputValidation(files, projectRoot) {
|
|
|
9439
9910
|
}
|
|
9440
9911
|
|
|
9441
9912
|
// src/security/checks/information-disclosure.ts
|
|
9442
|
-
import { readFileSync as
|
|
9443
|
-
import { join as
|
|
9913
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
9914
|
+
import { join as join22 } from "path";
|
|
9444
9915
|
var SKIP_DIRS6 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9445
9916
|
function shouldSkip6(filePath) {
|
|
9446
9917
|
return SKIP_DIRS6.some((d) => filePath.includes(d));
|
|
@@ -9452,7 +9923,7 @@ async function checkInformationDisclosure(files, projectRoot) {
|
|
|
9452
9923
|
if (shouldSkip6(file.filePath)) continue;
|
|
9453
9924
|
let content;
|
|
9454
9925
|
try {
|
|
9455
|
-
content =
|
|
9926
|
+
content = readFileSync18(join22(projectRoot, file.filePath), "utf-8");
|
|
9456
9927
|
} catch {
|
|
9457
9928
|
continue;
|
|
9458
9929
|
}
|
|
@@ -9522,8 +9993,8 @@ async function checkInformationDisclosure(files, projectRoot) {
|
|
|
9522
9993
|
}
|
|
9523
9994
|
|
|
9524
9995
|
// src/security/checks/cryptography.ts
|
|
9525
|
-
import { readFileSync as
|
|
9526
|
-
import { join as
|
|
9996
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
9997
|
+
import { join as join23 } from "path";
|
|
9527
9998
|
var SKIP_DIRS7 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9528
9999
|
function shouldSkip7(filePath) {
|
|
9529
10000
|
return SKIP_DIRS7.some((d) => filePath.includes(d));
|
|
@@ -9539,7 +10010,7 @@ async function checkCryptography(files, projectRoot) {
|
|
|
9539
10010
|
if (shouldSkip7(file.filePath)) continue;
|
|
9540
10011
|
let content;
|
|
9541
10012
|
try {
|
|
9542
|
-
content =
|
|
10013
|
+
content = readFileSync19(join23(projectRoot, file.filePath), "utf-8");
|
|
9543
10014
|
} catch {
|
|
9544
10015
|
continue;
|
|
9545
10016
|
}
|
|
@@ -9621,8 +10092,8 @@ async function checkCryptography(files, projectRoot) {
|
|
|
9621
10092
|
}
|
|
9622
10093
|
|
|
9623
10094
|
// src/security/checks/frontend.ts
|
|
9624
|
-
import { readFileSync as
|
|
9625
|
-
import { join as
|
|
10095
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
10096
|
+
import { join as join24 } from "path";
|
|
9626
10097
|
var SKIP_DIRS8 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
9627
10098
|
function shouldSkip8(filePath) {
|
|
9628
10099
|
return SKIP_DIRS8.some((d) => filePath.includes(d));
|
|
@@ -9638,7 +10109,7 @@ async function checkFrontend(files, projectRoot) {
|
|
|
9638
10109
|
if (!isFrontendFile(file.filePath)) continue;
|
|
9639
10110
|
let content;
|
|
9640
10111
|
try {
|
|
9641
|
-
content =
|
|
10112
|
+
content = readFileSync20(join24(projectRoot, file.filePath), "utf-8");
|
|
9642
10113
|
} catch {
|
|
9643
10114
|
continue;
|
|
9644
10115
|
}
|
|
@@ -10098,11 +10569,11 @@ async function scanSecurity(projectRoot, graph, options = {}) {
|
|
|
10098
10569
|
};
|
|
10099
10570
|
}
|
|
10100
10571
|
function detectPackageManager(projectRoot) {
|
|
10101
|
-
if (existsSync14(
|
|
10102
|
-
if (existsSync14(
|
|
10103
|
-
if (existsSync14(
|
|
10104
|
-
if (existsSync14(
|
|
10105
|
-
if (existsSync14(
|
|
10572
|
+
if (existsSync14(join25(projectRoot, "package.json"))) return "npm";
|
|
10573
|
+
if (existsSync14(join25(projectRoot, "requirements.txt"))) return "pip";
|
|
10574
|
+
if (existsSync14(join25(projectRoot, "pyproject.toml"))) return "pip";
|
|
10575
|
+
if (existsSync14(join25(projectRoot, "Cargo.toml"))) return "cargo";
|
|
10576
|
+
if (existsSync14(join25(projectRoot, "go.mod"))) return "go";
|
|
10106
10577
|
return "unknown";
|
|
10107
10578
|
}
|
|
10108
10579
|
|
|
@@ -10110,6 +10581,7 @@ export {
|
|
|
10110
10581
|
findProjectRoot,
|
|
10111
10582
|
parseTypeScriptFile,
|
|
10112
10583
|
parseProject,
|
|
10584
|
+
detectCrossLanguageEdges,
|
|
10113
10585
|
buildGraph,
|
|
10114
10586
|
findSymbols,
|
|
10115
10587
|
getDependencies,
|