@grwnd/pi-governance 1.7.0 → 1.9.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 +1 -0
- package/dist/extensions/index.cjs +43 -5
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.js +43 -5
- package/dist/extensions/index.js.map +1 -1
- package/dist/index.cjs +10 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ pi install npm:@grwnd/pi-governance
|
|
|
38
38
|
- **Audit** — Every decision logged as structured JSON
|
|
39
39
|
- **HITL** — Human approval for sensitive operations
|
|
40
40
|
- **Budgets** — Per-role tool invocation limits
|
|
41
|
+
- **Config self-protection** — Agents cannot modify their own governance files
|
|
41
42
|
|
|
42
43
|
## Customize
|
|
43
44
|
|
|
@@ -1551,15 +1551,15 @@ function sendJson(res, status, data) {
|
|
|
1551
1551
|
res.end(JSON.stringify(data));
|
|
1552
1552
|
}
|
|
1553
1553
|
function readBody(req) {
|
|
1554
|
-
return new Promise((
|
|
1554
|
+
return new Promise((resolve2, reject) => {
|
|
1555
1555
|
const chunks = [];
|
|
1556
1556
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
1557
|
-
req.on("end", () =>
|
|
1557
|
+
req.on("end", () => resolve2(Buffer.concat(chunks).toString("utf-8")));
|
|
1558
1558
|
req.on("error", reject);
|
|
1559
1559
|
});
|
|
1560
1560
|
}
|
|
1561
1561
|
function startWizardServer(options) {
|
|
1562
|
-
return new Promise((
|
|
1562
|
+
return new Promise((resolve2, reject) => {
|
|
1563
1563
|
let shutdownTimer;
|
|
1564
1564
|
const server = (0, import_node_http.createServer)((req, res) => {
|
|
1565
1565
|
setCorsHeaders(res);
|
|
@@ -1649,7 +1649,7 @@ function startWizardServer(options) {
|
|
|
1649
1649
|
shutdownTimer = setTimeout(() => {
|
|
1650
1650
|
closeServer();
|
|
1651
1651
|
}, AUTO_SHUTDOWN_MS);
|
|
1652
|
-
|
|
1652
|
+
resolve2({ port: addr.port, close: closeServer });
|
|
1653
1653
|
});
|
|
1654
1654
|
});
|
|
1655
1655
|
}
|
|
@@ -1708,6 +1708,7 @@ __export(extensions_exports, {
|
|
|
1708
1708
|
});
|
|
1709
1709
|
module.exports = __toCommonJS(extensions_exports);
|
|
1710
1710
|
var import_node_fs3 = require("fs");
|
|
1711
|
+
var import_node_path3 = require("path");
|
|
1711
1712
|
|
|
1712
1713
|
// src/lib/config/loader.ts
|
|
1713
1714
|
var import_fs = require("fs");
|
|
@@ -2181,7 +2182,16 @@ var DANGEROUS_PATTERNS = [
|
|
|
2181
2182
|
// Compiler/build (can execute arbitrary code)
|
|
2182
2183
|
/\bmake\s/,
|
|
2183
2184
|
/\bgcc\b/,
|
|
2184
|
-
/\bg
|
|
2185
|
+
/\bg\+\+/,
|
|
2186
|
+
// Governance config tampering — shell-based writes to governance files
|
|
2187
|
+
/(cat|echo|printf)\s.*>\s*.*governance(-rules)?\.yaml/,
|
|
2188
|
+
/\btee\s+.*governance(-rules)?\.yaml/,
|
|
2189
|
+
/sed\s+-i.*governance(-rules)?\.yaml/,
|
|
2190
|
+
/(cp|mv|rm)\s.*governance(-rules)?\.yaml/,
|
|
2191
|
+
/(cat|echo|printf)\s.*>\s*.*\.pi\/governance/,
|
|
2192
|
+
/\btee\s+.*\.pi\/governance/,
|
|
2193
|
+
/sed\s+-i.*\.pi\/governance/,
|
|
2194
|
+
/(cp|mv|rm)\s.*\.pi\/governance/
|
|
2185
2195
|
];
|
|
2186
2196
|
|
|
2187
2197
|
// src/lib/bash/classifier.ts
|
|
@@ -2976,7 +2986,9 @@ var piGovernance = (pi) => {
|
|
|
2976
2986
|
let configWatcher;
|
|
2977
2987
|
let dlpScanner;
|
|
2978
2988
|
let dlpMasker;
|
|
2989
|
+
let protectedPaths = /* @__PURE__ */ new Set();
|
|
2979
2990
|
const stats = {
|
|
2991
|
+
configTampered: 0,
|
|
2980
2992
|
allowed: 0,
|
|
2981
2993
|
denied: 0,
|
|
2982
2994
|
approvals: 0,
|
|
@@ -2990,6 +3002,15 @@ var piGovernance = (pi) => {
|
|
|
2990
3002
|
sessionId = ctx.sessionId;
|
|
2991
3003
|
const loaded = loadConfig();
|
|
2992
3004
|
config = loaded.config;
|
|
3005
|
+
const paths = /* @__PURE__ */ new Set();
|
|
3006
|
+
if (loaded.source !== "built-in") {
|
|
3007
|
+
paths.add((0, import_node_path3.resolve)(loaded.source));
|
|
3008
|
+
}
|
|
3009
|
+
const rulesFileCfg = config.policy?.yaml?.rules_file ?? "./governance-rules.yaml";
|
|
3010
|
+
paths.add((0, import_node_path3.resolve)(rulesFileCfg));
|
|
3011
|
+
paths.add((0, import_node_path3.resolve)(ctx.workingDirectory, ".pi/governance.yaml"));
|
|
3012
|
+
paths.add((0, import_node_path3.resolve)(ctx.workingDirectory, "governance-rules.yaml"));
|
|
3013
|
+
protectedPaths = paths;
|
|
2993
3014
|
const chain = createIdentityChain(config.auth);
|
|
2994
3015
|
identity = await chain.resolve();
|
|
2995
3016
|
const rulesFile = config.policy?.yaml?.rules_file ?? "./governance-rules.yaml";
|
|
@@ -3119,6 +3140,22 @@ var piGovernance = (pi) => {
|
|
|
3119
3140
|
tool: toolName,
|
|
3120
3141
|
input: params
|
|
3121
3142
|
};
|
|
3143
|
+
if (WRITE_TOOLS.has(toolName)) {
|
|
3144
|
+
const filePath = extractPath(toolName, input);
|
|
3145
|
+
if (filePath && protectedPaths.has((0, import_node_path3.resolve)(filePath))) {
|
|
3146
|
+
stats.configTampered++;
|
|
3147
|
+
await audit.log({
|
|
3148
|
+
...baseRecord,
|
|
3149
|
+
event: "config_tampered",
|
|
3150
|
+
decision: "denied",
|
|
3151
|
+
reason: `Config self-protection: write to governance file blocked (${filePath})`
|
|
3152
|
+
});
|
|
3153
|
+
return {
|
|
3154
|
+
block: true,
|
|
3155
|
+
reason: `Governance config files are protected and cannot be modified by agents`
|
|
3156
|
+
};
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3122
3159
|
if (executionMode === "dry_run") {
|
|
3123
3160
|
stats.dryRun++;
|
|
3124
3161
|
await audit.log({
|
|
@@ -3394,6 +3431,7 @@ var piGovernance = (pi) => {
|
|
|
3394
3431
|
` Approvals: ${stats.approvals}`,
|
|
3395
3432
|
` Dry-run blocks: ${stats.dryRun}`,
|
|
3396
3433
|
` Budget exceeded: ${stats.budgetExceeded}`,
|
|
3434
|
+
` Config tampered: ${stats.configTampered}`,
|
|
3397
3435
|
` DLP blocked: ${stats.dlpBlocked}`,
|
|
3398
3436
|
` DLP detected: ${stats.dlpDetected}`,
|
|
3399
3437
|
` DLP masked: ${stats.dlpMasked}`,
|