@node9/proxy 1.10.3 → 1.11.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 +5 -0
- package/dist/cli.js +802 -359
- package/dist/cli.mjs +800 -357
- package/dist/index.js +58 -14
- package/dist/index.mjs +58 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -261,6 +261,11 @@ var ConfigFileSchema = import_zod.z.object({
|
|
|
261
261
|
enabled: import_zod.z.boolean().optional(),
|
|
262
262
|
threshold: import_zod.z.number().min(2).optional(),
|
|
263
263
|
windowSeconds: import_zod.z.number().min(10).optional()
|
|
264
|
+
}).optional(),
|
|
265
|
+
skillPinning: import_zod.z.object({
|
|
266
|
+
enabled: import_zod.z.boolean().optional(),
|
|
267
|
+
mode: import_zod.z.enum(["warn", "block"]).optional(),
|
|
268
|
+
roots: import_zod.z.array(import_zod.z.string()).optional()
|
|
264
269
|
}).optional()
|
|
265
270
|
}).optional(),
|
|
266
271
|
environments: import_zod.z.record(import_zod.z.object({ requireApproval: import_zod.z.boolean().optional() })).optional()
|
|
@@ -618,7 +623,8 @@ var DEFAULT_CONFIG = {
|
|
|
618
623
|
}
|
|
619
624
|
],
|
|
620
625
|
dlp: { enabled: true, scanIgnoredTools: true },
|
|
621
|
-
loopDetection: { enabled: true, threshold: 5, windowSeconds: 120 }
|
|
626
|
+
loopDetection: { enabled: true, threshold: 5, windowSeconds: 120 },
|
|
627
|
+
skillPinning: { enabled: false, mode: "warn", roots: [] }
|
|
622
628
|
},
|
|
623
629
|
environments: {}
|
|
624
630
|
};
|
|
@@ -741,7 +747,11 @@ function getConfig(cwd) {
|
|
|
741
747
|
ignorePaths: [...DEFAULT_CONFIG.policy.snapshot.ignorePaths]
|
|
742
748
|
},
|
|
743
749
|
dlp: { ...DEFAULT_CONFIG.policy.dlp },
|
|
744
|
-
loopDetection: { ...DEFAULT_CONFIG.policy.loopDetection }
|
|
750
|
+
loopDetection: { ...DEFAULT_CONFIG.policy.loopDetection },
|
|
751
|
+
skillPinning: {
|
|
752
|
+
...DEFAULT_CONFIG.policy.skillPinning,
|
|
753
|
+
roots: [...DEFAULT_CONFIG.policy.skillPinning.roots]
|
|
754
|
+
}
|
|
745
755
|
};
|
|
746
756
|
const mergedEnvironments = { ...DEFAULT_CONFIG.environments };
|
|
747
757
|
const applyLayer = (source) => {
|
|
@@ -794,6 +804,16 @@ function getConfig(cwd) {
|
|
|
794
804
|
if (ld.windowSeconds !== void 0)
|
|
795
805
|
mergedPolicy.loopDetection.windowSeconds = ld.windowSeconds;
|
|
796
806
|
}
|
|
807
|
+
if (p.skillPinning && typeof p.skillPinning === "object") {
|
|
808
|
+
const sp = p.skillPinning;
|
|
809
|
+
if (sp.enabled !== void 0) mergedPolicy.skillPinning.enabled = sp.enabled;
|
|
810
|
+
if (sp.mode !== void 0) mergedPolicy.skillPinning.mode = sp.mode;
|
|
811
|
+
if (Array.isArray(sp.roots)) {
|
|
812
|
+
for (const r of sp.roots) {
|
|
813
|
+
if (typeof r === "string" && r.length > 0) mergedPolicy.skillPinning.roots.push(r);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
797
817
|
const envs = source.environments || {};
|
|
798
818
|
for (const [envName, envConfig] of Object.entries(envs)) {
|
|
799
819
|
if (envConfig && typeof envConfig === "object") {
|
|
@@ -845,6 +865,7 @@ function getConfig(cwd) {
|
|
|
845
865
|
mergedPolicy.sandboxPaths = [...new Set(mergedPolicy.sandboxPaths)];
|
|
846
866
|
mergedPolicy.dangerousWords = [...new Set(mergedPolicy.dangerousWords)];
|
|
847
867
|
mergedPolicy.ignoredTools = [...new Set(mergedPolicy.ignoredTools)];
|
|
868
|
+
mergedPolicy.skillPinning.roots = [...new Set(mergedPolicy.skillPinning.roots)];
|
|
848
869
|
mergedPolicy.snapshot.tools = [...new Set(mergedPolicy.snapshot.tools)];
|
|
849
870
|
mergedPolicy.snapshot.onlyPaths = [...new Set(mergedPolicy.snapshot.onlyPaths)];
|
|
850
871
|
mergedPolicy.snapshot.ignorePaths = [...new Set(mergedPolicy.snapshot.ignorePaths)];
|
|
@@ -1890,6 +1911,15 @@ var import_path9 = __toESM(require("path"));
|
|
|
1890
1911
|
var import_os6 = __toESM(require("os"));
|
|
1891
1912
|
var PAUSED_FILE = import_path9.default.join(import_os6.default.homedir(), ".node9", "PAUSED");
|
|
1892
1913
|
var TRUST_FILE = import_path9.default.join(import_os6.default.homedir(), ".node9", "trust.json");
|
|
1914
|
+
function extractCommandPattern(toolName, args) {
|
|
1915
|
+
const lower = toolName.toLowerCase();
|
|
1916
|
+
if (lower !== "bash" && lower !== "execute_bash" && lower !== "shell") return void 0;
|
|
1917
|
+
const a = args;
|
|
1918
|
+
const cmd = typeof a?.["command"] === "string" ? a["command"].trim() : "";
|
|
1919
|
+
if (!cmd) return void 0;
|
|
1920
|
+
const words = cmd.split(/\s+/);
|
|
1921
|
+
return words.slice(0, 2).join(" ");
|
|
1922
|
+
}
|
|
1893
1923
|
function checkPause() {
|
|
1894
1924
|
try {
|
|
1895
1925
|
if (!import_fs7.default.existsSync(PAUSED_FILE)) return { paused: false };
|
|
@@ -1913,7 +1943,7 @@ function atomicWriteSync(filePath, data, options) {
|
|
|
1913
1943
|
import_fs7.default.writeFileSync(tmpPath, data, options);
|
|
1914
1944
|
import_fs7.default.renameSync(tmpPath, filePath);
|
|
1915
1945
|
}
|
|
1916
|
-
function getActiveTrustSession(toolName) {
|
|
1946
|
+
function getActiveTrustSession(toolName, args) {
|
|
1917
1947
|
try {
|
|
1918
1948
|
if (!import_fs7.default.existsSync(TRUST_FILE)) return false;
|
|
1919
1949
|
const trust = JSON.parse(import_fs7.default.readFileSync(TRUST_FILE, "utf-8"));
|
|
@@ -1922,12 +1952,20 @@ function getActiveTrustSession(toolName) {
|
|
|
1922
1952
|
if (active.length !== trust.entries.length) {
|
|
1923
1953
|
import_fs7.default.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
|
|
1924
1954
|
}
|
|
1925
|
-
return active.some((e) =>
|
|
1955
|
+
return active.some((e) => {
|
|
1956
|
+
if (!(e.tool === toolName || matchesPattern(toolName, e.tool))) return false;
|
|
1957
|
+
if (e.commandPattern) {
|
|
1958
|
+
const actual = extractCommandPattern(toolName, args) ?? "";
|
|
1959
|
+
return actual === e.commandPattern || actual.startsWith(e.commandPattern + " ");
|
|
1960
|
+
}
|
|
1961
|
+
return true;
|
|
1962
|
+
});
|
|
1926
1963
|
} catch {
|
|
1927
1964
|
return false;
|
|
1928
1965
|
}
|
|
1929
1966
|
}
|
|
1930
|
-
function writeTrustSession(toolName, durationMs) {
|
|
1967
|
+
function writeTrustSession(toolName, durationMs, args) {
|
|
1968
|
+
const commandPattern = extractCommandPattern(toolName, args);
|
|
1931
1969
|
try {
|
|
1932
1970
|
let trust = { entries: [] };
|
|
1933
1971
|
try {
|
|
@@ -1937,8 +1975,14 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
1937
1975
|
} catch {
|
|
1938
1976
|
}
|
|
1939
1977
|
const now = Date.now();
|
|
1940
|
-
trust.entries = trust.entries.filter(
|
|
1941
|
-
|
|
1978
|
+
trust.entries = trust.entries.filter(
|
|
1979
|
+
(e) => !(e.tool === toolName && e.commandPattern === commandPattern) && e.expiry > now
|
|
1980
|
+
);
|
|
1981
|
+
trust.entries.push({
|
|
1982
|
+
tool: toolName,
|
|
1983
|
+
...commandPattern && { commandPattern },
|
|
1984
|
+
expiry: now + durationMs
|
|
1985
|
+
});
|
|
1942
1986
|
atomicWriteSync(TRUST_FILE, JSON.stringify(trust, null, 2));
|
|
1943
1987
|
} catch (err) {
|
|
1944
1988
|
if (process.env.NODE9_DEBUG === "1") {
|
|
@@ -2900,12 +2944,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2900
2944
|
};
|
|
2901
2945
|
}
|
|
2902
2946
|
}
|
|
2903
|
-
if (getActiveTrustSession(toolName)) {
|
|
2904
|
-
if (approvers.cloud && creds?.apiKey)
|
|
2905
|
-
await auditLocalAllow(toolName, args, "trust", creds, meta);
|
|
2906
|
-
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta, hashAuditArgs);
|
|
2907
|
-
return { approved: true, checkedBy: "trust" };
|
|
2908
|
-
}
|
|
2909
2947
|
const policyResult = await evaluatePolicy(toolName, args, meta?.agent);
|
|
2910
2948
|
if (policyResult.decision === "allow") {
|
|
2911
2949
|
if (approvers.cloud && creds?.apiKey)
|
|
@@ -2987,6 +3025,12 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2987
3025
|
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta, hashAuditArgs);
|
|
2988
3026
|
return { approved: true };
|
|
2989
3027
|
}
|
|
3028
|
+
if (!taintWarning && getActiveTrustSession(toolName, args)) {
|
|
3029
|
+
if (approvers.cloud && creds?.apiKey)
|
|
3030
|
+
await auditLocalAllow(toolName, args, "trust", creds, meta);
|
|
3031
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta, hashAuditArgs);
|
|
3032
|
+
return { approved: true, checkedBy: "trust" };
|
|
3033
|
+
}
|
|
2990
3034
|
if (taintWarning) {
|
|
2991
3035
|
explainableLabel = "\u{1F534} Node9 Taint (Exfiltration Prevention)";
|
|
2992
3036
|
riskMetadata = computeRiskMetadata(
|
|
@@ -3119,7 +3163,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3119
3163
|
riskMetadata?.ruleDescription
|
|
3120
3164
|
);
|
|
3121
3165
|
if (decision === "always_allow") {
|
|
3122
|
-
writeTrustSession(toolName, 36e5);
|
|
3166
|
+
writeTrustSession(toolName, 36e5, args);
|
|
3123
3167
|
return { approved: true, checkedBy: "trust" };
|
|
3124
3168
|
}
|
|
3125
3169
|
const isApproved = decision === "allow";
|
package/dist/index.mjs
CHANGED
|
@@ -231,6 +231,11 @@ var ConfigFileSchema = z.object({
|
|
|
231
231
|
enabled: z.boolean().optional(),
|
|
232
232
|
threshold: z.number().min(2).optional(),
|
|
233
233
|
windowSeconds: z.number().min(10).optional()
|
|
234
|
+
}).optional(),
|
|
235
|
+
skillPinning: z.object({
|
|
236
|
+
enabled: z.boolean().optional(),
|
|
237
|
+
mode: z.enum(["warn", "block"]).optional(),
|
|
238
|
+
roots: z.array(z.string()).optional()
|
|
234
239
|
}).optional()
|
|
235
240
|
}).optional(),
|
|
236
241
|
environments: z.record(z.object({ requireApproval: z.boolean().optional() })).optional()
|
|
@@ -588,7 +593,8 @@ var DEFAULT_CONFIG = {
|
|
|
588
593
|
}
|
|
589
594
|
],
|
|
590
595
|
dlp: { enabled: true, scanIgnoredTools: true },
|
|
591
|
-
loopDetection: { enabled: true, threshold: 5, windowSeconds: 120 }
|
|
596
|
+
loopDetection: { enabled: true, threshold: 5, windowSeconds: 120 },
|
|
597
|
+
skillPinning: { enabled: false, mode: "warn", roots: [] }
|
|
592
598
|
},
|
|
593
599
|
environments: {}
|
|
594
600
|
};
|
|
@@ -711,7 +717,11 @@ function getConfig(cwd) {
|
|
|
711
717
|
ignorePaths: [...DEFAULT_CONFIG.policy.snapshot.ignorePaths]
|
|
712
718
|
},
|
|
713
719
|
dlp: { ...DEFAULT_CONFIG.policy.dlp },
|
|
714
|
-
loopDetection: { ...DEFAULT_CONFIG.policy.loopDetection }
|
|
720
|
+
loopDetection: { ...DEFAULT_CONFIG.policy.loopDetection },
|
|
721
|
+
skillPinning: {
|
|
722
|
+
...DEFAULT_CONFIG.policy.skillPinning,
|
|
723
|
+
roots: [...DEFAULT_CONFIG.policy.skillPinning.roots]
|
|
724
|
+
}
|
|
715
725
|
};
|
|
716
726
|
const mergedEnvironments = { ...DEFAULT_CONFIG.environments };
|
|
717
727
|
const applyLayer = (source) => {
|
|
@@ -764,6 +774,16 @@ function getConfig(cwd) {
|
|
|
764
774
|
if (ld.windowSeconds !== void 0)
|
|
765
775
|
mergedPolicy.loopDetection.windowSeconds = ld.windowSeconds;
|
|
766
776
|
}
|
|
777
|
+
if (p.skillPinning && typeof p.skillPinning === "object") {
|
|
778
|
+
const sp = p.skillPinning;
|
|
779
|
+
if (sp.enabled !== void 0) mergedPolicy.skillPinning.enabled = sp.enabled;
|
|
780
|
+
if (sp.mode !== void 0) mergedPolicy.skillPinning.mode = sp.mode;
|
|
781
|
+
if (Array.isArray(sp.roots)) {
|
|
782
|
+
for (const r of sp.roots) {
|
|
783
|
+
if (typeof r === "string" && r.length > 0) mergedPolicy.skillPinning.roots.push(r);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
767
787
|
const envs = source.environments || {};
|
|
768
788
|
for (const [envName, envConfig] of Object.entries(envs)) {
|
|
769
789
|
if (envConfig && typeof envConfig === "object") {
|
|
@@ -815,6 +835,7 @@ function getConfig(cwd) {
|
|
|
815
835
|
mergedPolicy.sandboxPaths = [...new Set(mergedPolicy.sandboxPaths)];
|
|
816
836
|
mergedPolicy.dangerousWords = [...new Set(mergedPolicy.dangerousWords)];
|
|
817
837
|
mergedPolicy.ignoredTools = [...new Set(mergedPolicy.ignoredTools)];
|
|
838
|
+
mergedPolicy.skillPinning.roots = [...new Set(mergedPolicy.skillPinning.roots)];
|
|
818
839
|
mergedPolicy.snapshot.tools = [...new Set(mergedPolicy.snapshot.tools)];
|
|
819
840
|
mergedPolicy.snapshot.onlyPaths = [...new Set(mergedPolicy.snapshot.onlyPaths)];
|
|
820
841
|
mergedPolicy.snapshot.ignorePaths = [...new Set(mergedPolicy.snapshot.ignorePaths)];
|
|
@@ -1860,6 +1881,15 @@ import path9 from "path";
|
|
|
1860
1881
|
import os6 from "os";
|
|
1861
1882
|
var PAUSED_FILE = path9.join(os6.homedir(), ".node9", "PAUSED");
|
|
1862
1883
|
var TRUST_FILE = path9.join(os6.homedir(), ".node9", "trust.json");
|
|
1884
|
+
function extractCommandPattern(toolName, args) {
|
|
1885
|
+
const lower = toolName.toLowerCase();
|
|
1886
|
+
if (lower !== "bash" && lower !== "execute_bash" && lower !== "shell") return void 0;
|
|
1887
|
+
const a = args;
|
|
1888
|
+
const cmd = typeof a?.["command"] === "string" ? a["command"].trim() : "";
|
|
1889
|
+
if (!cmd) return void 0;
|
|
1890
|
+
const words = cmd.split(/\s+/);
|
|
1891
|
+
return words.slice(0, 2).join(" ");
|
|
1892
|
+
}
|
|
1863
1893
|
function checkPause() {
|
|
1864
1894
|
try {
|
|
1865
1895
|
if (!fs7.existsSync(PAUSED_FILE)) return { paused: false };
|
|
@@ -1883,7 +1913,7 @@ function atomicWriteSync(filePath, data, options) {
|
|
|
1883
1913
|
fs7.writeFileSync(tmpPath, data, options);
|
|
1884
1914
|
fs7.renameSync(tmpPath, filePath);
|
|
1885
1915
|
}
|
|
1886
|
-
function getActiveTrustSession(toolName) {
|
|
1916
|
+
function getActiveTrustSession(toolName, args) {
|
|
1887
1917
|
try {
|
|
1888
1918
|
if (!fs7.existsSync(TRUST_FILE)) return false;
|
|
1889
1919
|
const trust = JSON.parse(fs7.readFileSync(TRUST_FILE, "utf-8"));
|
|
@@ -1892,12 +1922,20 @@ function getActiveTrustSession(toolName) {
|
|
|
1892
1922
|
if (active.length !== trust.entries.length) {
|
|
1893
1923
|
fs7.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
|
|
1894
1924
|
}
|
|
1895
|
-
return active.some((e) =>
|
|
1925
|
+
return active.some((e) => {
|
|
1926
|
+
if (!(e.tool === toolName || matchesPattern(toolName, e.tool))) return false;
|
|
1927
|
+
if (e.commandPattern) {
|
|
1928
|
+
const actual = extractCommandPattern(toolName, args) ?? "";
|
|
1929
|
+
return actual === e.commandPattern || actual.startsWith(e.commandPattern + " ");
|
|
1930
|
+
}
|
|
1931
|
+
return true;
|
|
1932
|
+
});
|
|
1896
1933
|
} catch {
|
|
1897
1934
|
return false;
|
|
1898
1935
|
}
|
|
1899
1936
|
}
|
|
1900
|
-
function writeTrustSession(toolName, durationMs) {
|
|
1937
|
+
function writeTrustSession(toolName, durationMs, args) {
|
|
1938
|
+
const commandPattern = extractCommandPattern(toolName, args);
|
|
1901
1939
|
try {
|
|
1902
1940
|
let trust = { entries: [] };
|
|
1903
1941
|
try {
|
|
@@ -1907,8 +1945,14 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
1907
1945
|
} catch {
|
|
1908
1946
|
}
|
|
1909
1947
|
const now = Date.now();
|
|
1910
|
-
trust.entries = trust.entries.filter(
|
|
1911
|
-
|
|
1948
|
+
trust.entries = trust.entries.filter(
|
|
1949
|
+
(e) => !(e.tool === toolName && e.commandPattern === commandPattern) && e.expiry > now
|
|
1950
|
+
);
|
|
1951
|
+
trust.entries.push({
|
|
1952
|
+
tool: toolName,
|
|
1953
|
+
...commandPattern && { commandPattern },
|
|
1954
|
+
expiry: now + durationMs
|
|
1955
|
+
});
|
|
1912
1956
|
atomicWriteSync(TRUST_FILE, JSON.stringify(trust, null, 2));
|
|
1913
1957
|
} catch (err) {
|
|
1914
1958
|
if (process.env.NODE9_DEBUG === "1") {
|
|
@@ -2870,12 +2914,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2870
2914
|
};
|
|
2871
2915
|
}
|
|
2872
2916
|
}
|
|
2873
|
-
if (getActiveTrustSession(toolName)) {
|
|
2874
|
-
if (approvers.cloud && creds?.apiKey)
|
|
2875
|
-
await auditLocalAllow(toolName, args, "trust", creds, meta);
|
|
2876
|
-
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta, hashAuditArgs);
|
|
2877
|
-
return { approved: true, checkedBy: "trust" };
|
|
2878
|
-
}
|
|
2879
2917
|
const policyResult = await evaluatePolicy(toolName, args, meta?.agent);
|
|
2880
2918
|
if (policyResult.decision === "allow") {
|
|
2881
2919
|
if (approvers.cloud && creds?.apiKey)
|
|
@@ -2957,6 +2995,12 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2957
2995
|
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta, hashAuditArgs);
|
|
2958
2996
|
return { approved: true };
|
|
2959
2997
|
}
|
|
2998
|
+
if (!taintWarning && getActiveTrustSession(toolName, args)) {
|
|
2999
|
+
if (approvers.cloud && creds?.apiKey)
|
|
3000
|
+
await auditLocalAllow(toolName, args, "trust", creds, meta);
|
|
3001
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta, hashAuditArgs);
|
|
3002
|
+
return { approved: true, checkedBy: "trust" };
|
|
3003
|
+
}
|
|
2960
3004
|
if (taintWarning) {
|
|
2961
3005
|
explainableLabel = "\u{1F534} Node9 Taint (Exfiltration Prevention)";
|
|
2962
3006
|
riskMetadata = computeRiskMetadata(
|
|
@@ -3089,7 +3133,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
3089
3133
|
riskMetadata?.ruleDescription
|
|
3090
3134
|
);
|
|
3091
3135
|
if (decision === "always_allow") {
|
|
3092
|
-
writeTrustSession(toolName, 36e5);
|
|
3136
|
+
writeTrustSession(toolName, 36e5, args);
|
|
3093
3137
|
return { approved: true, checkedBy: "trust" };
|
|
3094
3138
|
}
|
|
3095
3139
|
const isApproved = decision === "allow";
|