feat(extensions): protected-paths, permission-gate, company-context

- protected-paths: glob-based block of write/edit to secrets/keys/.git/prod configs
- permission-gate: confirm dangerous bash (rm -rf, force push, push to protected branch, prod kubectl, docker prune, sudo)
- company-context: loads config/company.json (+ optional remoteConfigUrl w/ 2.5s timeout), injects corporate context via before_agent_start, registers /kit-config
- config path resolved via import.meta.url, not cwd; text template in company-context.md
This commit is contained in:
dev
2026-07-16 11:00:09 +03:00
parent 0725b6b034
commit 75e9bf85ba
4 changed files with 347 additions and 0 deletions
+92
View File
@@ -0,0 +1,92 @@
/**
* Protected Paths Extension
*
* WHAT: Blocks `write` and `edit` tool calls that target sensitive files
* (secrets, keys, VCS internals, production config). Based on the
* official protected-paths example, extended with glob matching.
*
* CONFIGURE: Edit the PROTECTED_PATHS constant below. Patterns are globs:
* `*` matches within one path segment,
* `**` matches across segments,
* `?` matches a single character.
* A leading `**/` means "in any directory".
*
* DISABLE: Remove/rename this file from the package `extensions/` folder,
* or exclude it via the package manifest / settings.
*/
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
// ---------------------------------------------------------------------------
// Protected path patterns. Add new entries here to widen protection.
// Keep this list conservative — it blocks writes agent-wide.
// ---------------------------------------------------------------------------
const PROTECTED_PATHS: string[] = [
".env*", // .env, .env.local, .env.production, ...
"**/secrets/**", // anything under a secrets/ directory
"**/*.pem", // TLS/cert private material
"**/*.key", // private keys
".git/**", // VCS internals
// Production environment config files (adjust to your conventions):
"**/*.prod.yml",
"**/*.prod.yaml",
"**/*.production.yml",
"**/*.production.yaml",
"**/values-prod.yaml", // Helm prod values
"**/config/prod*", // config/prod.json, config/production.*, ...
];
/**
* Compile a glob pattern into a RegExp that matches a path suffix, so the
* pattern matches regardless of whether the path is absolute or relative.
*/
function globToRegExp(glob: string): RegExp {
let g = glob;
if (g.startsWith("**/")) {
g = g.slice(3); // the "(^|/)" prefix already means "in any directory"
}
let re = "";
for (let i = 0; i < g.length; i++) {
const c = g[i];
if (c === "*") {
if (g[i + 1] === "*") {
re += ".*";
i++;
} else {
re += "[^/]*";
}
} else if (c === "?") {
re += "[^/]";
} else if ("\\^$+.()|{}[]".includes(c)) {
re += `\\${c}`;
} else {
re += c;
}
}
return new RegExp(`(^|/)${re}$`);
}
const COMPILED = PROTECTED_PATHS.map((p) => ({ glob: p, re: globToRegExp(p) }));
export default function protectedPathsExtension(pi: ExtensionAPI) {
pi.on("tool_call", async (event, ctx) => {
if (event.toolName !== "write" && event.toolName !== "edit") {
return undefined;
}
const raw = event.input.path as string | undefined;
if (!raw) return undefined;
const path = raw.replace(/\\/g, "/"); // normalize Windows separators
const hit = COMPILED.find((c) => c.re.test(path));
if (hit) {
if (ctx.hasUI) {
ctx.ui.notify(`Blocked write to protected path: ${raw} (matches "${hit.glob}")`, "warning");
}
return { block: true, reason: `Path "${raw}" is protected (pattern "${hit.glob}")` };
}
return undefined;
});
}