@lazyingart/agintiflow 0.20.88 → 0.20.89
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/package.json +1 -1
- package/scripts/smoke-coding-tools.js +14 -0
- package/src/command-policy.js +76 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.89",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Low-cost, project-aware Web and CLI agents with DeepSeek/Venice/OpenAI routing, visible tool calls, durable sessions, scouts, AAPS, SCS, and guarded local execution.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -276,6 +276,18 @@ try {
|
|
|
276
276
|
);
|
|
277
277
|
assert(readonlyProbePolicy.allowed, "read-only toolchain probe sequence should not require package-install-policy=allow");
|
|
278
278
|
assert(readonlyProbePolicy.category === "read-only", "read-only toolchain probe sequence should be classified as read-only");
|
|
279
|
+
const readonlyVersionPipelinePolicy = evaluateCommandPolicy(
|
|
280
|
+
"which pdflatex latexmk python3 2>&1; pdflatex --version 2>&1 | head -2; latexmk --version 2>&1 | head -2",
|
|
281
|
+
dockerWorkspaceNoInstallsPolicy
|
|
282
|
+
);
|
|
283
|
+
assert(readonlyVersionPipelinePolicy.allowed, "read-only version probe pipelines should not require package-install-policy=allow");
|
|
284
|
+
assert(readonlyVersionPipelinePolicy.category === "read-only", "read-only version probe pipelines should be classified as read-only");
|
|
285
|
+
const readonlyTestEchoPolicy = evaluateCommandPolicy(
|
|
286
|
+
'test -f /usr/bin/pdflatex && echo "pdflatex: FOUND" || echo "pdflatex: NOT FOUND"; test -f /usr/bin/latexmk && echo "latexmk: FOUND" || echo "latexmk: NOT FOUND"; python3 --version 2>&1',
|
|
287
|
+
dockerWorkspaceNoInstallsPolicy
|
|
288
|
+
);
|
|
289
|
+
assert(readonlyTestEchoPolicy.allowed, "read-only test/echo probe sequence should not require package-install-policy=allow");
|
|
290
|
+
assert(readonlyTestEchoPolicy.category === "read-only", "read-only test/echo probe sequence should be classified as read-only");
|
|
279
291
|
const curlPolicy = evaluateCommandPolicy("curl -s -o /dev/null -w '%{http_code}' https://github.com/lazyingart/AgInTiFlow.git", dockerWorkspacePolicy);
|
|
280
292
|
assert(curlPolicy.allowed, "curl URL probe with flags should be allowed in docker-workspace allow mode");
|
|
281
293
|
assert(curlPolicy.needsNetwork, "curl URL probe with flags was not classified as network");
|
|
@@ -736,6 +748,8 @@ try {
|
|
|
736
748
|
"auto_system_pro_route",
|
|
737
749
|
"auto_engineering_guidance",
|
|
738
750
|
"command_policy_readonly_probe_sequence_no_installs",
|
|
751
|
+
"command_policy_readonly_version_pipeline_no_installs",
|
|
752
|
+
"command_policy_readonly_test_echo_no_installs",
|
|
739
753
|
"command_policy_git_clone_network",
|
|
740
754
|
"command_policy_safe_chmod_sequence",
|
|
741
755
|
"command_policy_cd_workspace",
|
package/src/command-policy.js
CHANGED
|
@@ -23,6 +23,10 @@ const READ_ONLY_PATTERNS = [
|
|
|
23
23
|
/^python(?:3)?\s+--version$/,
|
|
24
24
|
/^pip(?:3)?\s+--version$/,
|
|
25
25
|
/^conda\s+--version$/,
|
|
26
|
+
/^(?:pdflatex|latexmk)\s+--version$/,
|
|
27
|
+
/^test\s+-[efdx]\s+[-\w./~]+$/,
|
|
28
|
+
/^true$/,
|
|
29
|
+
/^false$/,
|
|
26
30
|
/^echo(?:\s+.+)?$/,
|
|
27
31
|
];
|
|
28
32
|
|
|
@@ -392,13 +396,13 @@ function splitTopLevelShellSequence(command = "") {
|
|
|
392
396
|
quote = char;
|
|
393
397
|
continue;
|
|
394
398
|
}
|
|
395
|
-
if (char === ";" || (char === "&" && text[index + 1] === "&")) {
|
|
399
|
+
if (char === ";" || (char === "&" && text[index + 1] === "&") || (char === "|" && text[index + 1] === "|")) {
|
|
396
400
|
const part = current.trim();
|
|
397
401
|
if (!part) return null;
|
|
398
402
|
parts.push(part);
|
|
399
403
|
current = "";
|
|
400
404
|
hadSeparator = true;
|
|
401
|
-
if (char === "&") index += 1;
|
|
405
|
+
if (char === "&" || char === "|") index += 1;
|
|
402
406
|
continue;
|
|
403
407
|
}
|
|
404
408
|
current += char;
|
|
@@ -412,7 +416,7 @@ function splitTopLevelShellSequence(command = "") {
|
|
|
412
416
|
function classifyShellSequence(normalized) {
|
|
413
417
|
const parts = splitTopLevelShellSequence(normalized);
|
|
414
418
|
if (!parts) return null;
|
|
415
|
-
const classifications = parts.map((part) => classifyCdCommand(part) || classifySimpleCommand(part));
|
|
419
|
+
const classifications = parts.map((part) => classifyCdCommand(part) || classifyPipelineSequence(part) || classifySimpleCommand(part));
|
|
416
420
|
const blocked = classifications.find((classification) => classification.category === "blocked" || classification.category === "destructive");
|
|
417
421
|
if (blocked) return blocked;
|
|
418
422
|
const broad = classifications.find((classification) => classification.category === "general-shell");
|
|
@@ -446,6 +450,73 @@ function classifyShellSequence(normalized) {
|
|
|
446
450
|
};
|
|
447
451
|
}
|
|
448
452
|
|
|
453
|
+
function splitTopLevelPipeline(command = "") {
|
|
454
|
+
const parts = [];
|
|
455
|
+
let current = "";
|
|
456
|
+
let quote = "";
|
|
457
|
+
let escaped = false;
|
|
458
|
+
let hadPipe = false;
|
|
459
|
+
const text = String(command || "");
|
|
460
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
461
|
+
const char = text[index];
|
|
462
|
+
if (escaped) {
|
|
463
|
+
current += char;
|
|
464
|
+
escaped = false;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (char === "\\") {
|
|
468
|
+
current += char;
|
|
469
|
+
escaped = true;
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (quote) {
|
|
473
|
+
current += char;
|
|
474
|
+
if (char === quote) quote = "";
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
if (char === "'" || char === '"') {
|
|
478
|
+
current += char;
|
|
479
|
+
quote = char;
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
if (char === "|" && text[index + 1] !== "|") {
|
|
483
|
+
const part = current.trim();
|
|
484
|
+
if (!part) return null;
|
|
485
|
+
parts.push(part);
|
|
486
|
+
current = "";
|
|
487
|
+
hadPipe = true;
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
current += char;
|
|
491
|
+
}
|
|
492
|
+
const finalPart = current.trim();
|
|
493
|
+
if (finalPart) parts.push(finalPart);
|
|
494
|
+
if (!hadPipe || parts.length < 2) return null;
|
|
495
|
+
return parts;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function classifyPipelineSequence(normalized) {
|
|
499
|
+
const parts = splitTopLevelPipeline(normalized);
|
|
500
|
+
if (!parts) return null;
|
|
501
|
+
const classifications = parts.map((part) => classifyCdCommand(part) || classifySimpleCommand(part));
|
|
502
|
+
const blocked = classifications.find((classification) => classification.category === "blocked" || classification.category === "destructive");
|
|
503
|
+
if (blocked) return blocked;
|
|
504
|
+
if (classifications.every((classification) => classification.category === "read-only")) {
|
|
505
|
+
return {
|
|
506
|
+
category: "read-only",
|
|
507
|
+
needsNetwork: false,
|
|
508
|
+
writesWorkspace: false,
|
|
509
|
+
reason: `Read-only shell pipeline: ${normalized}`,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
category: "general-shell",
|
|
514
|
+
needsNetwork: classifications.some((classification) => classification.needsNetwork),
|
|
515
|
+
writesWorkspace: classifications.some((classification) => classification.writesWorkspace),
|
|
516
|
+
reason: `Shell pipeline includes a broad segment and requires trusted shell policy: ${normalized}`,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
|
|
449
520
|
function classifyCdCommand(normalized) {
|
|
450
521
|
const match = normalized.match(/^cd\s+([-\w./]+)\s+&&\s+(.+)$/);
|
|
451
522
|
if (!match) return null;
|
|
@@ -454,7 +525,7 @@ function classifyCdCommand(normalized) {
|
|
|
454
525
|
if (!isSafeRelativeDir(dir) && !virtualWorkspacePath) {
|
|
455
526
|
return { category: "blocked", reason: `cd target must be a safe workspace-relative directory: ${dir}` };
|
|
456
527
|
}
|
|
457
|
-
const innerClassification = classifySimpleCommand(inner.trim());
|
|
528
|
+
const innerClassification = classifyShellSequence(inner.trim()) || classifyPipelineSequence(inner.trim()) || classifySimpleCommand(inner.trim());
|
|
458
529
|
if (innerClassification.category === "blocked") return innerClassification;
|
|
459
530
|
return { ...innerClassification, cdDir: dir, virtualWorkspacePath };
|
|
460
531
|
}
|
|
@@ -463,7 +534,7 @@ export function classifyCommand(command) {
|
|
|
463
534
|
const normalized = String(command || "").trim();
|
|
464
535
|
if (!normalized) return { category: "blocked", reason: "Command is empty." };
|
|
465
536
|
|
|
466
|
-
return classifyCdCommand(normalized) || classifyShellSequence(normalized) || classifySimpleCommand(normalized);
|
|
537
|
+
return classifyCdCommand(normalized) || classifyShellSequence(normalized) || classifyPipelineSequence(normalized) || classifySimpleCommand(normalized);
|
|
467
538
|
}
|
|
468
539
|
|
|
469
540
|
export function evaluateCommandPolicy(command, config) {
|