@lazyingart/agintiflow 0.20.221 → 0.20.223
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 +34 -0
- package/src/command-policy.js +25 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.223",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -658,6 +658,21 @@ try {
|
|
|
658
658
|
exactCompoundReadOnlyAuditPolicy.writesWorkspace === false,
|
|
659
659
|
"a finite read-only prelude and conditional directory audit incorrectly required destructive host permission"
|
|
660
660
|
);
|
|
661
|
+
const exactReadOnlyExistenceLoopWithSuffix =
|
|
662
|
+
'for f in scripts/xyq_cdp_browser.py scripts/xyq_chrome/launch_chrome.sh scripts/xyq_chrome/watch_thread_dom_download.py scripts/musia_mv_finalize.sh; do test -e "/home/lachlan/ProjectsLFS/LALACHAN/$f" && echo "OK $f" || echo "MISS $f"; done; echo "---mv_packs---"; ls -1 /home/lachlan/ProjectsLFS/Musia/data/mv_packs 2>&1; echo "---creative_projects---"; ls -1 /home/lachlan/ProjectsLFS/Musia/data/creative_projects 2>&1 | head -20';
|
|
663
|
+
const exactReadOnlyExistenceLoopWithSuffixPolicy = evaluateCommandPolicy(
|
|
664
|
+
exactReadOnlyExistenceLoopWithSuffix,
|
|
665
|
+
hostReadRootPolicy
|
|
666
|
+
);
|
|
667
|
+
assert(
|
|
668
|
+
exactReadOnlyExistenceLoopWithSuffixPolicy.allowed &&
|
|
669
|
+
exactReadOnlyExistenceLoopWithSuffixPolicy.category === "read-only" &&
|
|
670
|
+
exactReadOnlyExistenceLoopWithSuffixPolicy.boundedForLoop === true &&
|
|
671
|
+
exactReadOnlyExistenceLoopWithSuffixPolicy.boundedCompoundSequence === true &&
|
|
672
|
+
exactReadOnlyExistenceLoopWithSuffixPolicy.needsNetwork === false &&
|
|
673
|
+
exactReadOnlyExistenceLoopWithSuffixPolicy.writesWorkspace === false,
|
|
674
|
+
"a finite read-only existence loop with bounded list suffix incorrectly required destructive host permission"
|
|
675
|
+
);
|
|
661
676
|
const exactReadOnlyGitIdentityProbe =
|
|
662
677
|
'git status --short && echo "TOPLEVEL=$(git rev-parse --show-toplevel 2>&1)" && echo "BRANCH=$(git rev-parse --abbrev-ref HEAD 2>&1)"';
|
|
663
678
|
const exactReadOnlyGitIdentityProbePolicy = evaluateCommandPolicy(
|
|
@@ -685,6 +700,20 @@ try {
|
|
|
685
700
|
exactReadOnlyGitIdentityLoopPolicy.writesWorkspace === false,
|
|
686
701
|
"bounded per-repository Git identity/status audit incorrectly required destructive host permission"
|
|
687
702
|
);
|
|
703
|
+
const exactReadOnlyGitRepositoryAudit =
|
|
704
|
+
'for d in /home/lachlan/ProjectsLFS/AgenticApp /home/lachlan/ProjectsLFS/Musia /home/lachlan/ProjectsLFS/LALACHAN /home/lachlan/DiskMech/Projects/lazyedit; do echo "=== $d ==="; git -C "$d" config --get user.name; git -C "$d" config --get user.email; git -C "$d" status --porcelain=v1 --branch; echo "--- remotes ---"; git -C "$d" remote -v; echo; done';
|
|
705
|
+
const exactReadOnlyGitRepositoryAuditPolicy = evaluateCommandPolicy(
|
|
706
|
+
exactReadOnlyGitRepositoryAudit,
|
|
707
|
+
hostReadRootPolicy
|
|
708
|
+
);
|
|
709
|
+
assert(
|
|
710
|
+
exactReadOnlyGitRepositoryAuditPolicy.allowed &&
|
|
711
|
+
exactReadOnlyGitRepositoryAuditPolicy.category === "read-only" &&
|
|
712
|
+
exactReadOnlyGitRepositoryAuditPolicy.boundedForLoop === true &&
|
|
713
|
+
exactReadOnlyGitRepositoryAuditPolicy.needsNetwork === false &&
|
|
714
|
+
exactReadOnlyGitRepositoryAuditPolicy.writesWorkspace === false,
|
|
715
|
+
"bounded per-repository Git identity/status/remote audit incorrectly required destructive host permission"
|
|
716
|
+
);
|
|
688
717
|
for (const unsafeEchoSubstitution of [
|
|
689
718
|
'echo "VALUE=$(rm -rf report.md)"',
|
|
690
719
|
'echo "VALUE=$(cp README.md report.md)"',
|
|
@@ -716,6 +745,8 @@ try {
|
|
|
716
745
|
'for d in /tmp; do echo "VALUE=$(git -C "$d" config --global user.name)"; done',
|
|
717
746
|
'for d in /tmp; do echo "VALUE=$(git -C "$d" status; rm -rf report.md)"; done',
|
|
718
747
|
'for d in /tmp; do git -C "$d" status --short > report.md; done',
|
|
748
|
+
'for d in /tmp; do git -C "$d" remote add origin https://example.com/repo.git; done',
|
|
749
|
+
'for d in /tmp; do git -C "$d" remote set-url origin https://example.com/repo.git; done',
|
|
719
750
|
]) {
|
|
720
751
|
const unsafeLoopSubstitutionPolicy = evaluateCommandPolicy(
|
|
721
752
|
unsafeLoopSubstitution,
|
|
@@ -738,6 +769,9 @@ try {
|
|
|
738
769
|
'echo start; for d in /tmp; do if [ -d "$d" ]; then if [ -f "$d/x" ]; then cat "$d/x"; else echo missing; fi; else echo absent; fi; done',
|
|
739
770
|
'echo start; for d in $ROOTS; do if [ -d "$d" ]; then ls "$d"; else echo missing; fi; done',
|
|
740
771
|
'echo start; for d in /tmp; do if [ -d "$d" ]; then ls "$d"; else echo missing; fi; done; rm -rf report.md',
|
|
772
|
+
'for f in README.md; do test -e "$f" && echo ok || echo missing; done; cp README.md copied.md',
|
|
773
|
+
'for f in README.md; do test -e "$f" && echo ok || echo missing; done; curl https://example.com',
|
|
774
|
+
'for f in README.md; do test -e "$f" && echo ok || echo missing; done; echo done > report.md',
|
|
741
775
|
]) {
|
|
742
776
|
const unsafeCompoundAuditPolicy = evaluateCommandPolicy(unsafeCompoundAudit, hostReadRootPolicy);
|
|
743
777
|
assert(
|
package/src/command-policy.js
CHANGED
|
@@ -1158,7 +1158,8 @@ function classifyScopedReadOnlyGitProbe(normalized = "") {
|
|
|
1158
1158
|
(args.length === 1 && ["user.name", "user.email"].includes(args[0])) ||
|
|
1159
1159
|
(args.length === 2 && args[0] === "--get" && ["user.name", "user.email"].includes(args[1]))
|
|
1160
1160
|
);
|
|
1161
|
-
|
|
1161
|
+
const safeRemoteList = operation === "remote" && args.length === 1 && args[0] === "-v";
|
|
1162
|
+
if (!safeStatus && !safeIdentityConfig && !safeRemoteList) return null;
|
|
1162
1163
|
|
|
1163
1164
|
return {
|
|
1164
1165
|
category: "read-only",
|
|
@@ -1356,7 +1357,11 @@ function classifySimpleCommand(normalized) {
|
|
|
1356
1357
|
};
|
|
1357
1358
|
}
|
|
1358
1359
|
|
|
1359
|
-
if (
|
|
1360
|
+
if (
|
|
1361
|
+
matchAny(READ_ONLY_PATTERNS, commandForPatternMatching) ||
|
|
1362
|
+
isReadOnlyFindCommand(normalized) ||
|
|
1363
|
+
(!hasActiveShellExpansion(benignRedirectCommand) && isReadOnlyShellCondition(benignRedirectCommand))
|
|
1364
|
+
) {
|
|
1360
1365
|
return {
|
|
1361
1366
|
category: "read-only",
|
|
1362
1367
|
needsNetwork: false,
|
|
@@ -1766,7 +1771,7 @@ function classifyReadOnlyForLoop(normalized) {
|
|
|
1766
1771
|
|
|
1767
1772
|
function classifyReadOnlyCompoundSequence(normalized) {
|
|
1768
1773
|
const text = String(normalized || "").trim();
|
|
1769
|
-
if (!text ||
|
|
1774
|
+
if (!text || hasActiveShellCommandSubstitution(text)) return null;
|
|
1770
1775
|
|
|
1771
1776
|
let forIndex = findUnquotedShellWord(text, "for");
|
|
1772
1777
|
const startsAtCommandBoundary = (index) => {
|
|
@@ -1777,16 +1782,24 @@ function classifyReadOnlyCompoundSequence(normalized) {
|
|
|
1777
1782
|
while (forIndex > 0 && !startsAtCommandBoundary(forIndex)) {
|
|
1778
1783
|
forIndex = findUnquotedShellWord(text, "for", forIndex + 3);
|
|
1779
1784
|
}
|
|
1780
|
-
if (forIndex
|
|
1785
|
+
if (forIndex < 0) return null;
|
|
1781
1786
|
|
|
1782
1787
|
const prefixSource = trimShellListBoundary(text.slice(0, forIndex));
|
|
1783
|
-
const
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1788
|
+
const doneIndex = findUnquotedShellWord(text, "done", forIndex + 3);
|
|
1789
|
+
if (doneIndex < 0) return null;
|
|
1790
|
+
const loopSource = text.slice(forIndex, doneIndex + 4).trim();
|
|
1791
|
+
const suffixSource = trimShellListBoundary(text.slice(doneIndex + 4));
|
|
1792
|
+
if (!prefixSource && !suffixSource) return null;
|
|
1793
|
+
|
|
1794
|
+
for (const [label, source] of [["prelude", prefixSource], ["suffix", suffixSource]]) {
|
|
1795
|
+
if (!source) continue;
|
|
1796
|
+
const classification = classifyReadOnlyCommandList(source);
|
|
1797
|
+
if (classification.category === "blocked" || classification.category === "destructive") {
|
|
1798
|
+
return { ...classification, gitOnly: false };
|
|
1799
|
+
}
|
|
1800
|
+
if (classification.category !== "read-only" || classification.writesWorkspace) {
|
|
1801
|
+
return broadForLoopClassification(text, `the command ${label} is not read-only`);
|
|
1802
|
+
}
|
|
1790
1803
|
}
|
|
1791
1804
|
const loopClassification = classifyReadOnlyForLoop(loopSource);
|
|
1792
1805
|
if (!loopClassification) return null;
|
|
@@ -1800,7 +1813,7 @@ function classifyReadOnlyCompoundSequence(normalized) {
|
|
|
1800
1813
|
gitOnly: false,
|
|
1801
1814
|
boundedForLoop: true,
|
|
1802
1815
|
boundedCompoundSequence: true,
|
|
1803
|
-
reason: "
|
|
1816
|
+
reason: "A bounded read-only shell loop has only finite read-only command context.",
|
|
1804
1817
|
};
|
|
1805
1818
|
}
|
|
1806
1819
|
|