debug-toolkit 0.4.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 +236 -0
- package/SKILL.md +104 -0
- package/dist/capture.d.ts +46 -0
- package/dist/capture.js +246 -0
- package/dist/capture.js.map +1 -0
- package/dist/cleanup.d.ts +12 -0
- package/dist/cleanup.js +103 -0
- package/dist/cleanup.js.map +1 -0
- package/dist/cli.d.ts +40 -0
- package/dist/cli.js +109 -0
- package/dist/cli.js.map +1 -0
- package/dist/context.d.ts +59 -0
- package/dist/context.js +338 -0
- package/dist/context.js.map +1 -0
- package/dist/demo.d.ts +12 -0
- package/dist/demo.js +347 -0
- package/dist/demo.js.map +1 -0
- package/dist/hook.d.ts +17 -0
- package/dist/hook.js +106 -0
- package/dist/hook.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +203 -0
- package/dist/index.js.map +1 -0
- package/dist/injected.js +151 -0
- package/dist/instrument.d.ts +15 -0
- package/dist/instrument.js +87 -0
- package/dist/instrument.js.map +1 -0
- package/dist/mcp.d.ts +14 -0
- package/dist/mcp.js +420 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory.d.ts +73 -0
- package/dist/memory.js +291 -0
- package/dist/memory.js.map +1 -0
- package/dist/methodology.d.ts +7 -0
- package/dist/methodology.js +100 -0
- package/dist/methodology.js.map +1 -0
- package/dist/proxy.d.ts +12 -0
- package/dist/proxy.js +168 -0
- package/dist/proxy.js.map +1 -0
- package/dist/security.d.ts +27 -0
- package/dist/security.js +158 -0
- package/dist/security.js.map +1 -0
- package/dist/session.d.ts +53 -0
- package/dist/session.js +94 -0
- package/dist/session.js.map +1 -0
- package/package.json +33 -0
package/dist/security.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { resolve, relative } from "node:path";
|
|
2
|
+
import { realpathSync, existsSync, readFileSync, writeFileSync, appendFileSync } from "node:fs";
|
|
3
|
+
// --- Path traversal protection ---
|
|
4
|
+
/**
|
|
5
|
+
* Validate that a file path is within the project root.
|
|
6
|
+
* Resolves symlinks and rejects paths that escape the boundary.
|
|
7
|
+
*/
|
|
8
|
+
export function validateFilePath(filePath, projectRoot) {
|
|
9
|
+
// Resolve the project root to its real path (handles symlinks like /tmp → /private/tmp)
|
|
10
|
+
const realRoot = existsSync(projectRoot) ? realpathSync(projectRoot) : resolve(projectRoot);
|
|
11
|
+
// Resolve filePath: if absolute, resolve it independently then check containment
|
|
12
|
+
const rawResolved = resolve(projectRoot, filePath);
|
|
13
|
+
const resolved = existsSync(rawResolved) ? realpathSync(rawResolved) : resolve(realRoot, relative(resolve(projectRoot), rawResolved));
|
|
14
|
+
// Check the resolved path is within project root
|
|
15
|
+
const rel = relative(realRoot, resolved);
|
|
16
|
+
if (rel.startsWith("..") || resolve(rel) === rel) {
|
|
17
|
+
throw new Error(`Security: path "${filePath}" resolves outside project root "${projectRoot}"`);
|
|
18
|
+
}
|
|
19
|
+
// If file exists, also check the real path (resolves symlinks)
|
|
20
|
+
if (existsSync(resolved)) {
|
|
21
|
+
const real = realpathSync(resolved);
|
|
22
|
+
const realRel = relative(realRoot, real);
|
|
23
|
+
if (realRel.startsWith("..")) {
|
|
24
|
+
throw new Error(`Security: symlink "${filePath}" points outside project root`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Reject system paths (only if project root is NOT inside them)
|
|
28
|
+
const dangerous = ["/etc/", "/usr/", "/bin/", "/sbin/", "/var/", "/root/"];
|
|
29
|
+
for (const prefix of dangerous) {
|
|
30
|
+
if (resolved.startsWith(prefix) && !realRoot.startsWith(prefix.slice(0, -1))) {
|
|
31
|
+
throw new Error(`Security: refusing to access system path "${resolved}"`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return resolved;
|
|
35
|
+
}
|
|
36
|
+
// --- Command injection protection ---
|
|
37
|
+
const SHELL_METACHAR_RE = /[;&|`$(){}[\]!<>\\]/;
|
|
38
|
+
/**
|
|
39
|
+
* Validate a command string for obvious injection attempts.
|
|
40
|
+
* We allow commands but reject chaining operators.
|
|
41
|
+
*/
|
|
42
|
+
export function validateCommand(command) {
|
|
43
|
+
const trimmed = command.trim();
|
|
44
|
+
if (!trimmed)
|
|
45
|
+
throw new Error("Security: empty command");
|
|
46
|
+
// Block obvious shell injection patterns
|
|
47
|
+
if (trimmed.includes("&&") || trimmed.includes("||") || trimmed.includes(";")) {
|
|
48
|
+
// Allow these only if they look like a normal dev command
|
|
49
|
+
// e.g., "npm run build && npm test" is fine
|
|
50
|
+
// But "; rm -rf /" is not
|
|
51
|
+
const parts = trimmed.split(/[;&|]+/).map((s) => s.trim());
|
|
52
|
+
for (const part of parts) {
|
|
53
|
+
if (part.startsWith("rm ") || part.startsWith("curl ") ||
|
|
54
|
+
part.includes("> /") || part.includes("| sh") ||
|
|
55
|
+
part.includes("eval ") || part.includes("exec ")) {
|
|
56
|
+
throw new Error(`Security: potentially dangerous command "${part}"`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return trimmed;
|
|
61
|
+
}
|
|
62
|
+
// --- Sensitive data redaction ---
|
|
63
|
+
const REDACTION_PATTERNS = [
|
|
64
|
+
// Authorization headers in captured output
|
|
65
|
+
[/(?:Authorization|Bearer|Token|X-API-Key)[:=]\s*["']?[A-Za-z0-9._\-/+=]{8,}["']?/gi, "[REDACTED_AUTH]"],
|
|
66
|
+
// JWT tokens
|
|
67
|
+
[/eyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}/g, "[REDACTED_JWT]"],
|
|
68
|
+
// Common secret patterns in env vars and output
|
|
69
|
+
[/(?:password|passwd|secret|api_key|apikey|access_token|private_key|credentials?)[\s=:]+["']?[^\s"']{8,}["']?/gi, "[REDACTED_SECRET]"],
|
|
70
|
+
// AWS keys
|
|
71
|
+
[/(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}/g, "[REDACTED_AWS_KEY]"],
|
|
72
|
+
// Connection strings with credentials
|
|
73
|
+
[/(?:postgres|mysql|mongodb|redis):\/\/[^:]+:[^@]+@/gi, "[REDACTED_CONNECTION_STRING]://***@"],
|
|
74
|
+
// GitHub tokens
|
|
75
|
+
[/gh[pousr]_[A-Za-z0-9_]{36,}/g, "[REDACTED_GITHUB_TOKEN]"],
|
|
76
|
+
// npm tokens
|
|
77
|
+
[/npm_[A-Za-z0-9]{36,}/g, "[REDACTED_NPM_TOKEN]"],
|
|
78
|
+
// Generic hex secrets (32+ chars, likely API keys)
|
|
79
|
+
[/(?:key|token|secret|password)["']?\s*[:=]\s*["']?[a-f0-9]{32,}["']?/gi, "[REDACTED_HEX_SECRET]"],
|
|
80
|
+
];
|
|
81
|
+
/**
|
|
82
|
+
* Redact sensitive information from captured text.
|
|
83
|
+
*/
|
|
84
|
+
export function redactSensitiveData(text) {
|
|
85
|
+
let result = text;
|
|
86
|
+
for (const [pattern, replacement] of REDACTION_PATTERNS) {
|
|
87
|
+
result = result.replace(pattern, replacement);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Redact sensitive headers from captured network requests.
|
|
93
|
+
*/
|
|
94
|
+
export function redactHeaders(headers) {
|
|
95
|
+
const sensitiveHeaders = new Set([
|
|
96
|
+
"authorization",
|
|
97
|
+
"cookie",
|
|
98
|
+
"set-cookie",
|
|
99
|
+
"x-api-key",
|
|
100
|
+
"x-auth-token",
|
|
101
|
+
"proxy-authorization",
|
|
102
|
+
]);
|
|
103
|
+
const redacted = {};
|
|
104
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
105
|
+
if (sensitiveHeaders.has(key.toLowerCase())) {
|
|
106
|
+
redacted[key] = "[REDACTED]";
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
redacted[key] = value;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return redacted;
|
|
113
|
+
}
|
|
114
|
+
// --- Auto .gitignore management ---
|
|
115
|
+
/**
|
|
116
|
+
* Ensure .debug/ is in .gitignore.
|
|
117
|
+
*/
|
|
118
|
+
export function ensureGitignore(projectRoot) {
|
|
119
|
+
const gitignorePath = resolve(projectRoot, ".gitignore");
|
|
120
|
+
if (existsSync(gitignorePath)) {
|
|
121
|
+
const content = readFileSync(gitignorePath, "utf-8");
|
|
122
|
+
if (content.includes(".debug/") || content.includes(".debug")) {
|
|
123
|
+
return; // Already ignored
|
|
124
|
+
}
|
|
125
|
+
appendFileSync(gitignorePath, "\n# debug-toolkit session data\n.debug/\n");
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
writeFileSync(gitignorePath, "# debug-toolkit session data\n.debug/\n");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// --- Expression sanitization ---
|
|
132
|
+
/**
|
|
133
|
+
* Validate that an instrumentation expression is safe to inject.
|
|
134
|
+
* Prevents code injection via the expression parameter.
|
|
135
|
+
*/
|
|
136
|
+
export function validateExpression(expression) {
|
|
137
|
+
const trimmed = expression.trim();
|
|
138
|
+
if (!trimmed)
|
|
139
|
+
throw new Error("Empty expression");
|
|
140
|
+
// Block only actual code execution patterns, not property access
|
|
141
|
+
const dangerous = [
|
|
142
|
+
/\beval\s*\(/,
|
|
143
|
+
/\bFunction\s*\(/,
|
|
144
|
+
/\bprocess\.exit\s*\(/,
|
|
145
|
+
/\brequire\s*\(\s*['"`]/,
|
|
146
|
+
/\bimport\s*\(\s*['"`]/,
|
|
147
|
+
/\bexec\s*\(/,
|
|
148
|
+
/\bexecSync\s*\(/,
|
|
149
|
+
/\bspawn\s*\(/,
|
|
150
|
+
];
|
|
151
|
+
for (const pattern of dangerous) {
|
|
152
|
+
if (pattern.test(trimmed)) {
|
|
153
|
+
throw new Error(`Security: expression "${trimmed}" contains potentially dangerous code`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return trimmed;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface Hypothesis {
|
|
2
|
+
id: string;
|
|
3
|
+
text: string;
|
|
4
|
+
status: "testing" | "confirmed" | "rejected";
|
|
5
|
+
evidence: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface InstrumentationRecord {
|
|
8
|
+
id: string;
|
|
9
|
+
filePath: string;
|
|
10
|
+
lineNumber: number;
|
|
11
|
+
markerTag: string;
|
|
12
|
+
language: string;
|
|
13
|
+
insertedCode: string;
|
|
14
|
+
active: boolean;
|
|
15
|
+
hypothesisId: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface Capture {
|
|
18
|
+
id: string;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
source: "terminal" | "browser-console" | "browser-network" | "browser-error" | "environment" | "tauri-log";
|
|
21
|
+
markerTag: string | null;
|
|
22
|
+
data: unknown;
|
|
23
|
+
hypothesisId: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface FileSnapshot {
|
|
26
|
+
filePath: string;
|
|
27
|
+
content: string;
|
|
28
|
+
takenAt: string;
|
|
29
|
+
}
|
|
30
|
+
export interface DebugSession {
|
|
31
|
+
id: string;
|
|
32
|
+
version: number;
|
|
33
|
+
createdAt: string;
|
|
34
|
+
status: "active" | "resolved" | "abandoned";
|
|
35
|
+
problem: string;
|
|
36
|
+
hypotheses: Hypothesis[];
|
|
37
|
+
instrumentation: InstrumentationRecord[];
|
|
38
|
+
captures: Capture[];
|
|
39
|
+
snapshots: Record<string, FileSnapshot>;
|
|
40
|
+
diagnosis: string | null;
|
|
41
|
+
_markerIndex: Record<string, string>;
|
|
42
|
+
}
|
|
43
|
+
export declare const MAX_FILE_SIZE: number;
|
|
44
|
+
export declare function createSession(cwd: string, problem: string): DebugSession;
|
|
45
|
+
export declare function saveSession(cwd: string, session: DebugSession): void;
|
|
46
|
+
export declare function loadSession(cwd: string, sessionId: string): DebugSession;
|
|
47
|
+
export declare function indexMarker(session: DebugSession, tag: string, hypothesisId: string | null): void;
|
|
48
|
+
export declare function lookupHypothesis(session: DebugSession, markerTag: string): string | null;
|
|
49
|
+
export declare function newHypothesisId(): string;
|
|
50
|
+
export declare function newInstrumentationId(): string;
|
|
51
|
+
export declare function newCaptureId(): string;
|
|
52
|
+
export declare function nextMarkerTag(): string;
|
|
53
|
+
export declare function resetMarkerCounter(): void;
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { ensureGitignore } from "./security.js";
|
|
5
|
+
// --- Constants ---
|
|
6
|
+
export const MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
7
|
+
const MAX_CAPTURES = 3000;
|
|
8
|
+
const TRIM_TO = 2000;
|
|
9
|
+
// --- Helpers ---
|
|
10
|
+
function uid(prefix) {
|
|
11
|
+
return `${prefix}_${randomBytes(4).toString("hex")}`;
|
|
12
|
+
}
|
|
13
|
+
function debugDir(cwd) {
|
|
14
|
+
return join(cwd, ".debug");
|
|
15
|
+
}
|
|
16
|
+
function sessionsDir(cwd) {
|
|
17
|
+
return join(debugDir(cwd), "sessions");
|
|
18
|
+
}
|
|
19
|
+
function ensureDirs(cwd) {
|
|
20
|
+
for (const d of [debugDir(cwd), sessionsDir(cwd)]) {
|
|
21
|
+
if (!existsSync(d))
|
|
22
|
+
mkdirSync(d, { recursive: true, mode: 0o700 });
|
|
23
|
+
}
|
|
24
|
+
ensureGitignore(cwd);
|
|
25
|
+
}
|
|
26
|
+
function atomicWrite(filePath, data) {
|
|
27
|
+
const tmp = `${filePath}.tmp_${process.pid}`;
|
|
28
|
+
writeFileSync(tmp, data, { mode: 0o600 });
|
|
29
|
+
renameSync(tmp, filePath);
|
|
30
|
+
}
|
|
31
|
+
// --- Session CRUD ---
|
|
32
|
+
export function createSession(cwd, problem) {
|
|
33
|
+
ensureDirs(cwd);
|
|
34
|
+
const session = {
|
|
35
|
+
id: uid("dbg"),
|
|
36
|
+
version: 0,
|
|
37
|
+
createdAt: new Date().toISOString(),
|
|
38
|
+
status: "active",
|
|
39
|
+
problem,
|
|
40
|
+
hypotheses: [],
|
|
41
|
+
instrumentation: [],
|
|
42
|
+
captures: [],
|
|
43
|
+
snapshots: {},
|
|
44
|
+
diagnosis: null,
|
|
45
|
+
_markerIndex: {},
|
|
46
|
+
};
|
|
47
|
+
saveSession(cwd, session);
|
|
48
|
+
return session;
|
|
49
|
+
}
|
|
50
|
+
export function saveSession(cwd, session) {
|
|
51
|
+
ensureDirs(cwd);
|
|
52
|
+
// Trim captures to prevent unbounded growth
|
|
53
|
+
if (session.captures.length > MAX_CAPTURES) {
|
|
54
|
+
session.captures = session.captures.slice(-TRIM_TO);
|
|
55
|
+
}
|
|
56
|
+
session.version++;
|
|
57
|
+
atomicWrite(join(sessionsDir(cwd), `${session.id}.json`), JSON.stringify(session, null, 2));
|
|
58
|
+
}
|
|
59
|
+
export function loadSession(cwd, sessionId) {
|
|
60
|
+
const p = join(sessionsDir(cwd), `${sessionId}.json`);
|
|
61
|
+
if (!existsSync(p))
|
|
62
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
63
|
+
const session = JSON.parse(readFileSync(p, "utf-8"));
|
|
64
|
+
// Rebuild marker index if missing (backwards compat)
|
|
65
|
+
if (!session._markerIndex) {
|
|
66
|
+
session._markerIndex = {};
|
|
67
|
+
for (const r of session.instrumentation) {
|
|
68
|
+
if (r.active && r.hypothesisId) {
|
|
69
|
+
session._markerIndex[r.markerTag] = r.hypothesisId;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return session;
|
|
74
|
+
}
|
|
75
|
+
// --- Marker index ---
|
|
76
|
+
export function indexMarker(session, tag, hypothesisId) {
|
|
77
|
+
if (hypothesisId) {
|
|
78
|
+
session._markerIndex[tag] = hypothesisId;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function lookupHypothesis(session, markerTag) {
|
|
82
|
+
return session._markerIndex[markerTag] ?? null;
|
|
83
|
+
}
|
|
84
|
+
// --- ID generators ---
|
|
85
|
+
export function newHypothesisId() { return uid("hyp"); }
|
|
86
|
+
export function newInstrumentationId() { return uid("ins"); }
|
|
87
|
+
export function newCaptureId() { return uid("cap"); }
|
|
88
|
+
let markerCounter = 0;
|
|
89
|
+
export function nextMarkerTag() {
|
|
90
|
+
markerCounter++;
|
|
91
|
+
return `DBG_${String(markerCounter).padStart(3, "0")}`;
|
|
92
|
+
}
|
|
93
|
+
export function resetMarkerCounter() { markerCounter = 0; }
|
|
94
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "debug-toolkit",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Closed-loop debugging toolkit for AI coding agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"debug-toolkit": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc && cp src/injected.js dist/injected.js",
|
|
11
|
+
"dev": "tsc --watch",
|
|
12
|
+
"start": "node dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"hooks",
|
|
17
|
+
"SKILL.md"
|
|
18
|
+
],
|
|
19
|
+
"keywords": ["debugging", "ai", "mcp", "developer-tools"],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
23
|
+
"http-proxy": "^1.18.1",
|
|
24
|
+
"tree-kill": "^1.2.2",
|
|
25
|
+
"ws": "^8.18.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/http-proxy": "^1.17.16",
|
|
29
|
+
"@types/node": "^22.13.0",
|
|
30
|
+
"@types/ws": "^8.5.14",
|
|
31
|
+
"typescript": "^5.7.0"
|
|
32
|
+
}
|
|
33
|
+
}
|