@wrongstack/tools 0.3.3 → 0.3.4
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/dist/builtin.js +105 -37
- package/dist/builtin.js.map +1 -1
- package/dist/exec.js +37 -1
- package/dist/exec.js.map +1 -1
- package/dist/index.js +105 -37
- package/dist/index.js.map +1 -1
- package/dist/install.js +16 -0
- package/dist/install.js.map +1 -1
- package/dist/logs.js +9 -0
- package/dist/logs.js.map +1 -1
- package/dist/pack.js +105 -37
- package/dist/pack.js.map +1 -1
- package/dist/scaffold.js +10 -3
- package/dist/scaffold.js.map +1 -1
- package/package.json +2 -2
package/dist/builtin.js
CHANGED
|
@@ -61,8 +61,8 @@ async function* spawnStream(opts) {
|
|
|
61
61
|
let spawnFailed = false;
|
|
62
62
|
for (; ; ) {
|
|
63
63
|
while (queue.length === 0) {
|
|
64
|
-
await new Promise((
|
|
65
|
-
waiter =
|
|
64
|
+
await new Promise((resolve5) => {
|
|
65
|
+
waiter = resolve5;
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
const chunk = queue.shift();
|
|
@@ -351,10 +351,10 @@ var bashTool = {
|
|
|
351
351
|
queue.push(c);
|
|
352
352
|
}
|
|
353
353
|
};
|
|
354
|
-
const next = () => new Promise((
|
|
354
|
+
const next = () => new Promise((resolve5) => {
|
|
355
355
|
const c = queue.shift();
|
|
356
|
-
if (c)
|
|
357
|
-
else resolveNext =
|
|
356
|
+
if (c) resolve5(c);
|
|
357
|
+
else resolveNext = resolve5;
|
|
358
358
|
});
|
|
359
359
|
let lastFlush = Date.now();
|
|
360
360
|
const flush = () => {
|
|
@@ -589,7 +589,7 @@ function findGitDir(cwd) {
|
|
|
589
589
|
return null;
|
|
590
590
|
}
|
|
591
591
|
function runGit(args, cwd, signal) {
|
|
592
|
-
return new Promise((
|
|
592
|
+
return new Promise((resolve5) => {
|
|
593
593
|
let stdout = "";
|
|
594
594
|
let stderr = "";
|
|
595
595
|
const child = spawn("git", args, { cwd, signal, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -599,8 +599,8 @@ function runGit(args, cwd, signal) {
|
|
|
599
599
|
child.stderr?.on("data", (c) => {
|
|
600
600
|
stderr += c.toString();
|
|
601
601
|
});
|
|
602
|
-
child.on("close", (code) =>
|
|
603
|
-
child.on("error", (e) =>
|
|
602
|
+
child.on("close", (code) => resolve5({ stdout, stderr, exitCode: code ?? 0 }));
|
|
603
|
+
child.on("error", (e) => resolve5({ stdout: "", stderr: e.message, exitCode: 1 }));
|
|
604
604
|
});
|
|
605
605
|
}
|
|
606
606
|
async function fileDiff(input, ctx, signal) {
|
|
@@ -934,7 +934,7 @@ var ALLOWED_COMMANDS = {
|
|
|
934
934
|
cargo: ["--version", "build", "test", "check"],
|
|
935
935
|
rustc: ["--version"],
|
|
936
936
|
go: ["version", "run", "build", "test"],
|
|
937
|
-
python: ["--version"
|
|
937
|
+
python: ["--version"],
|
|
938
938
|
pip: ["--version", "install", "list"],
|
|
939
939
|
docker: ["--version", "ps", "images", "build"],
|
|
940
940
|
kubectl: ["version", "get", "describe", "logs"]
|
|
@@ -942,6 +942,30 @@ var ALLOWED_COMMANDS = {
|
|
|
942
942
|
var MAX_ARGS = 20;
|
|
943
943
|
var MAX_OUTPUT2 = 2e5;
|
|
944
944
|
var TIMEOUT_MS = 3e4;
|
|
945
|
+
var BLOCKED_ARG_PATTERNS = {
|
|
946
|
+
// python -c/--command executes arbitrary code; python -m runs modules
|
|
947
|
+
python: [/-c$/, /^--command$/, /^-m$/, /^--module$/],
|
|
948
|
+
// git --exec=<cmd> runs arbitrary commands via upload-pack/receive-pack
|
|
949
|
+
git: [/^--exec=/, /^--upload-pack=/, /^--receive-pack=/],
|
|
950
|
+
// node -r/--require preloads arbitrary modules; --eval executes code
|
|
951
|
+
node: [/^-r$/, /^--require$/, /^-e$/, /^--eval$/, /^--prof-process$/],
|
|
952
|
+
// go run could execute arbitrary .go files; -ldflags could inject build-time code
|
|
953
|
+
go: [/^-ldflags$/],
|
|
954
|
+
// bun --preload is similar to node --require
|
|
955
|
+
bun: [/^--preload$/]
|
|
956
|
+
};
|
|
957
|
+
function validateArgs(cmd, args) {
|
|
958
|
+
const blocked = BLOCKED_ARG_PATTERNS[cmd];
|
|
959
|
+
if (!blocked) return null;
|
|
960
|
+
for (const arg of args) {
|
|
961
|
+
for (const pattern of blocked) {
|
|
962
|
+
if (pattern.test(arg)) {
|
|
963
|
+
return `Blocked argument "${arg}" for command "${cmd}" (matches security pattern ${pattern})`;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return null;
|
|
968
|
+
}
|
|
945
969
|
var execTool = {
|
|
946
970
|
name: "exec",
|
|
947
971
|
category: "Shell",
|
|
@@ -985,6 +1009,18 @@ var execTool = {
|
|
|
985
1009
|
}
|
|
986
1010
|
const args = (input.args ?? []).slice(0, MAX_ARGS);
|
|
987
1011
|
const timeout = Math.max(1, Math.min(input.timeout ?? TIMEOUT_MS, TIMEOUT_MS));
|
|
1012
|
+
const argError = validateArgs(cmd, args);
|
|
1013
|
+
if (argError) {
|
|
1014
|
+
return {
|
|
1015
|
+
command: cmd,
|
|
1016
|
+
args,
|
|
1017
|
+
stdout: "",
|
|
1018
|
+
stderr: argError,
|
|
1019
|
+
exitCode: 1,
|
|
1020
|
+
truncated: false,
|
|
1021
|
+
allowed: false
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
988
1024
|
const requestedCwd = input.cwd ? path.resolve(ctx.projectRoot, input.cwd) : ctx.cwd;
|
|
989
1025
|
const rel = path.relative(ctx.projectRoot, requestedCwd);
|
|
990
1026
|
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
@@ -1004,7 +1040,7 @@ var execTool = {
|
|
|
1004
1040
|
}
|
|
1005
1041
|
};
|
|
1006
1042
|
function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
1007
|
-
return new Promise((
|
|
1043
|
+
return new Promise((resolve5) => {
|
|
1008
1044
|
let stdout = "";
|
|
1009
1045
|
let stderr = "";
|
|
1010
1046
|
let killed = false;
|
|
@@ -1026,7 +1062,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
1026
1062
|
});
|
|
1027
1063
|
child.on("close", (code) => {
|
|
1028
1064
|
clearTimeout(timer);
|
|
1029
|
-
|
|
1065
|
+
resolve5({
|
|
1030
1066
|
command: cmd,
|
|
1031
1067
|
args,
|
|
1032
1068
|
stdout: stdout.slice(0, MAX_OUTPUT2),
|
|
@@ -1038,7 +1074,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
1038
1074
|
});
|
|
1039
1075
|
child.on("error", (err) => {
|
|
1040
1076
|
clearTimeout(timer);
|
|
1041
|
-
|
|
1077
|
+
resolve5({
|
|
1042
1078
|
command: cmd,
|
|
1043
1079
|
args,
|
|
1044
1080
|
stdout: stdout.slice(0, MAX_OUTPUT2),
|
|
@@ -1565,7 +1601,7 @@ function buildArgs(input) {
|
|
|
1565
1601
|
}
|
|
1566
1602
|
}
|
|
1567
1603
|
function runGit2(args, cwd, signal) {
|
|
1568
|
-
return new Promise((
|
|
1604
|
+
return new Promise((resolve5) => {
|
|
1569
1605
|
let stdout = "";
|
|
1570
1606
|
let stderr = "";
|
|
1571
1607
|
const child = spawn("git", args, {
|
|
@@ -1584,7 +1620,7 @@ function runGit2(args, cwd, signal) {
|
|
|
1584
1620
|
}
|
|
1585
1621
|
});
|
|
1586
1622
|
child.on("error", (err) => {
|
|
1587
|
-
|
|
1623
|
+
resolve5({
|
|
1588
1624
|
command: args[0],
|
|
1589
1625
|
stdout,
|
|
1590
1626
|
stderr: err.message,
|
|
@@ -1593,7 +1629,7 @@ function runGit2(args, cwd, signal) {
|
|
|
1593
1629
|
});
|
|
1594
1630
|
});
|
|
1595
1631
|
child.on("close", (code) => {
|
|
1596
|
-
|
|
1632
|
+
resolve5({
|
|
1597
1633
|
command: args[0],
|
|
1598
1634
|
stdout: stdout.slice(0, MAX_OUTPUT3),
|
|
1599
1635
|
stderr: stderr.slice(0, MAX_OUTPUT3),
|
|
@@ -1774,13 +1810,13 @@ var grepTool = {
|
|
|
1774
1810
|
}
|
|
1775
1811
|
};
|
|
1776
1812
|
async function detectRg(signal) {
|
|
1777
|
-
return new Promise((
|
|
1813
|
+
return new Promise((resolve5) => {
|
|
1778
1814
|
try {
|
|
1779
1815
|
const p = spawn("rg", ["--version"], { stdio: "ignore", signal });
|
|
1780
|
-
p.on("error", () =>
|
|
1781
|
-
p.on("close", (code) =>
|
|
1816
|
+
p.on("error", () => resolve5(false));
|
|
1817
|
+
p.on("close", (code) => resolve5(code === 0));
|
|
1782
1818
|
} catch {
|
|
1783
|
-
|
|
1819
|
+
resolve5(false);
|
|
1784
1820
|
}
|
|
1785
1821
|
});
|
|
1786
1822
|
}
|
|
@@ -2039,6 +2075,22 @@ var installTool = {
|
|
|
2039
2075
|
const pkgList = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map(
|
|
2040
2076
|
(p) => p.trim()
|
|
2041
2077
|
) : [];
|
|
2078
|
+
const PKG_NAME_RE = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/i;
|
|
2079
|
+
for (const pkg of pkgList) {
|
|
2080
|
+
if (!PKG_NAME_RE.test(pkg) || pkg.startsWith("-")) {
|
|
2081
|
+
yield {
|
|
2082
|
+
type: "final",
|
|
2083
|
+
output: {
|
|
2084
|
+
packages: pkgList,
|
|
2085
|
+
exit_code: 1,
|
|
2086
|
+
output: `Invalid package name "${pkg}". Names must match ${PKG_NAME_RE} and not start with "-".`,
|
|
2087
|
+
dry_run: Boolean(input.dry_run),
|
|
2088
|
+
truncated: false
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2042
2094
|
if (pkgList.length > 0) args.push(...pkgList);
|
|
2043
2095
|
yield {
|
|
2044
2096
|
type: "log",
|
|
@@ -2361,8 +2413,17 @@ var logsTool = {
|
|
|
2361
2413
|
async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
2362
2414
|
const args = ["logs"];
|
|
2363
2415
|
if (lines > 0) args.push("--tail", String(lines));
|
|
2416
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._:-]+$/.test(service)) {
|
|
2417
|
+
return {
|
|
2418
|
+
source: `docker:${service}`,
|
|
2419
|
+
entries: [],
|
|
2420
|
+
total: 0,
|
|
2421
|
+
truncated: false,
|
|
2422
|
+
stream_mode: false
|
|
2423
|
+
};
|
|
2424
|
+
}
|
|
2364
2425
|
args.push("--timestamps", service);
|
|
2365
|
-
return new Promise((
|
|
2426
|
+
return new Promise((resolve5) => {
|
|
2366
2427
|
let stdout = "";
|
|
2367
2428
|
let stderr = "";
|
|
2368
2429
|
const MAX = 2e5;
|
|
@@ -2376,7 +2437,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
2376
2437
|
child.on("close", (code) => {
|
|
2377
2438
|
const output = stdout + stderr;
|
|
2378
2439
|
const entries = parseLogLines(output, filterRe);
|
|
2379
|
-
|
|
2440
|
+
resolve5({
|
|
2380
2441
|
source: `docker:${service}`,
|
|
2381
2442
|
entries,
|
|
2382
2443
|
total: entries.length,
|
|
@@ -2386,7 +2447,7 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
|
2386
2447
|
});
|
|
2387
2448
|
child.on(
|
|
2388
2449
|
"error",
|
|
2389
|
-
(e) =>
|
|
2450
|
+
(e) => resolve5({
|
|
2390
2451
|
source: `docker:${service}`,
|
|
2391
2452
|
entries: [],
|
|
2392
2453
|
total: 0,
|
|
@@ -2520,7 +2581,7 @@ async function detectManager2(cwd) {
|
|
|
2520
2581
|
return "npm";
|
|
2521
2582
|
}
|
|
2522
2583
|
function runOutdated(manager, args, cwd, signal) {
|
|
2523
|
-
return new Promise((
|
|
2584
|
+
return new Promise((resolve5) => {
|
|
2524
2585
|
let stdout = "";
|
|
2525
2586
|
let stderr = "";
|
|
2526
2587
|
const MAX = 1e5;
|
|
@@ -2533,11 +2594,11 @@ function runOutdated(manager, args, cwd, signal) {
|
|
|
2533
2594
|
});
|
|
2534
2595
|
child.on("close", (code) => {
|
|
2535
2596
|
const result = parseOutdatedOutput(stdout, code ?? 0);
|
|
2536
|
-
|
|
2597
|
+
resolve5(result);
|
|
2537
2598
|
});
|
|
2538
2599
|
child.on(
|
|
2539
2600
|
"error",
|
|
2540
|
-
(e) =>
|
|
2601
|
+
(e) => resolve5({
|
|
2541
2602
|
exit_code: 1,
|
|
2542
2603
|
packages: [],
|
|
2543
2604
|
total: 0,
|
|
@@ -2667,7 +2728,7 @@ function stripPathComponents(p, strip) {
|
|
|
2667
2728
|
return parts.slice(strip).join("/");
|
|
2668
2729
|
}
|
|
2669
2730
|
function runPatch(args, cwd, signal) {
|
|
2670
|
-
return new Promise((
|
|
2731
|
+
return new Promise((resolve5) => {
|
|
2671
2732
|
let stdout = "";
|
|
2672
2733
|
let stderr = "";
|
|
2673
2734
|
const env = { ...buildChildEnv(), LANG: "C", LC_ALL: "C" };
|
|
@@ -2678,8 +2739,8 @@ function runPatch(args, cwd, signal) {
|
|
|
2678
2739
|
child.stderr?.on("data", (c) => {
|
|
2679
2740
|
stderr += c.toString();
|
|
2680
2741
|
});
|
|
2681
|
-
child.on("close", (code) =>
|
|
2682
|
-
child.on("error", (e) =>
|
|
2742
|
+
child.on("close", (code) => resolve5({ exitCode: code ?? 1, stdout, stderr }));
|
|
2743
|
+
child.on("error", (e) => resolve5({ exitCode: 1, stdout: "", stderr: e.message }));
|
|
2683
2744
|
});
|
|
2684
2745
|
}
|
|
2685
2746
|
function extractPatchedFiles(output) {
|
|
@@ -2975,13 +3036,13 @@ async function globFiles(pattern, base, extraGlob) {
|
|
|
2975
3036
|
return await globNative(pattern, base, extraGlob);
|
|
2976
3037
|
}
|
|
2977
3038
|
function checkRg() {
|
|
2978
|
-
return new Promise((
|
|
3039
|
+
return new Promise((resolve5) => {
|
|
2979
3040
|
try {
|
|
2980
3041
|
const p = spawn("rg", ["--version"], { stdio: "ignore" });
|
|
2981
|
-
p.on("error", () =>
|
|
2982
|
-
p.on("close", (code) =>
|
|
3042
|
+
p.on("error", () => resolve5(false));
|
|
3043
|
+
p.on("close", (code) => resolve5(code === 0));
|
|
2983
3044
|
} catch {
|
|
2984
|
-
|
|
3045
|
+
resolve5(false);
|
|
2985
3046
|
}
|
|
2986
3047
|
});
|
|
2987
3048
|
}
|
|
@@ -2993,10 +3054,10 @@ function spawnRgFind(pattern, base) {
|
|
|
2993
3054
|
buf += chunk.toString();
|
|
2994
3055
|
});
|
|
2995
3056
|
return {
|
|
2996
|
-
promise: new Promise((
|
|
3057
|
+
promise: new Promise((resolve5, reject) => {
|
|
2997
3058
|
child.on("error", reject);
|
|
2998
3059
|
child.on("close", () => {
|
|
2999
|
-
|
|
3060
|
+
resolve5(buf.split("\n").filter(Boolean));
|
|
3000
3061
|
});
|
|
3001
3062
|
})
|
|
3002
3063
|
};
|
|
@@ -3160,7 +3221,7 @@ var scaffoldTool = {
|
|
|
3160
3221
|
const vars = { name, ...input.vars };
|
|
3161
3222
|
const builtIn = BUILT_IN_TEMPLATES[input.template];
|
|
3162
3223
|
if (builtIn) {
|
|
3163
|
-
return await handleBuiltIn(name, builtIn.files, cwd, input.dry_run ?? false, vars);
|
|
3224
|
+
return await handleBuiltIn(name, builtIn.files, cwd, ctx, input.dry_run ?? false, vars);
|
|
3164
3225
|
}
|
|
3165
3226
|
return {
|
|
3166
3227
|
template: input.template,
|
|
@@ -3172,12 +3233,19 @@ var scaffoldTool = {
|
|
|
3172
3233
|
};
|
|
3173
3234
|
}
|
|
3174
3235
|
};
|
|
3175
|
-
async function handleBuiltIn(name, templateFiles, cwd, dryRun, vars) {
|
|
3236
|
+
async function handleBuiltIn(name, templateFiles, cwd, ctx, dryRun, vars) {
|
|
3176
3237
|
const files = [];
|
|
3177
3238
|
let filesCreated = 0;
|
|
3178
3239
|
for (const [filePath, content] of Object.entries(templateFiles)) {
|
|
3179
3240
|
const resolvedPath = substituteVars(filePath, name, vars);
|
|
3180
|
-
const
|
|
3241
|
+
const joinedPath = path.join(cwd, resolvedPath);
|
|
3242
|
+
const root = path.resolve(ctx.projectRoot);
|
|
3243
|
+
const target = path.resolve(joinedPath);
|
|
3244
|
+
const rel = path.relative(root, target);
|
|
3245
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
3246
|
+
throw new Error(`scaffold: generated path "${resolvedPath}" would escape project root`);
|
|
3247
|
+
}
|
|
3248
|
+
const fullPath = target;
|
|
3181
3249
|
if (!dryRun) {
|
|
3182
3250
|
await fs9.mkdir(path.dirname(fullPath), { recursive: true });
|
|
3183
3251
|
await fs9.writeFile(fullPath, substituteVars(content, name, vars), "utf8");
|