Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@brycehanscomb/toolgate",
"version": "0.4.0",
"version": "0.10.0",
"devDependencies": {
"bun-types": "latest"
},
Expand Down
47 changes: 47 additions & 0 deletions policies/_test-runners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Shared allowlist of test-runner command prefixes. Used by policies that
* permit running a test runner in various contexts (docker compose exec,
* subshell-wrapped cd, etc.).
*
* Each entry is an ordered token prefix. A command matches if its first
* N tokens equal the prefix exactly; the rest are treated as opaque args.
*/
export const TEST_RUNNER_PREFIXES: readonly (readonly string[])[] = [
// PHP / Laravel
["php", "artisan", "test"],
["php", "vendor/bin/phpunit"],
["php", "vendor/bin/pest"],
["php", "vendor/bin/phpstan"],
["vendor/bin/phpunit"],
["vendor/bin/pest"],
["vendor/bin/phpstan"],
["./vendor/bin/phpunit"],
["./vendor/bin/pest"],
["./vendor/bin/phpstan"],
// JS / TS
["bun", "test"],
["npm", "test"],
["pnpm", "test"],
["yarn", "test"],
// Python
["pytest"],
["python", "-m", "pytest"],
["python", "-m", "unittest"],
["python3", "-m", "pytest"],
["python3", "-m", "unittest"],
];

export function matchesTestRunner(tokens: string[], start = 0): boolean {
for (const prefix of TEST_RUNNER_PREFIXES) {
if (tokens.length - start < prefix.length) continue;
let ok = true;
for (let i = 0; i < prefix.length; i++) {
if (tokens[start + i] !== prefix[i]) {
ok = false;
break;
}
}
if (ok) return true;
}
return false;
}
5 changes: 3 additions & 2 deletions policies/allow-aws-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,13 @@ export function createAwsCliPolicy(config: AwsCliPolicyConfig = {}): Policy {
return {
name: "Allow AWS CLI",
description:
"Auto-allows non-destructive AWS CLI commands with ReadOnly profiles; requires approval for Admin profiles; denies destructive commands",
"Auto-allows non-destructive AWS CLI commands with ReadOnly profiles (via --profile or AWS_PROFILE env); requires approval for Admin profiles; denies destructive commands",
handler: async (call) => {
const tokens = await safeBashCommandOrPipeline(call);
if (!tokens || tokens[0] !== "aws") return next();

const profile = extractProfile(tokens);
// --profile flag wins, then AWS_PROFILE env, matching AWS CLI's own precedence
const profile = extractProfile(tokens) ?? call.context.env.AWS_PROFILE ?? null;

// Destructive commands — always require approval
if (isDestructiveCommand(tokens, extraDestructive)) return next();
Expand Down
49 changes: 49 additions & 0 deletions policies/allow-cdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { allow, next, type Policy } from "../src";
import { safeBashCommandOrPipeline } from "./parse-bash-ast";

/** CDK subcommands that are read-only / informational */
const SAFE_SUBCOMMANDS = new Set([
"ls",
"list",
"diff",
"synth",
"synthesize",
"doctor",
"context",
"metadata",
"notices",
]);

/** CDK flags that act as read-only subcommands */
const SAFE_FLAGS = new Set([
"--version",
"-v",
"--help",
"-h",
]);

const allowCdk: Policy = {
name: "Allow cdk read-only",
description:
"Auto-allows read-only cdk commands (ls, diff, synth, doctor, etc.); requires approval for deploy, destroy, bootstrap, import, migrate, rollback, watch",
handler: async (call) => {
const tokens = await safeBashCommandOrPipeline(call);
if (!tokens || tokens[0] !== "cdk") return next();

if (tokens.length >= 2 && SAFE_FLAGS.has(tokens[1])) return allow();

let subcommand: string | undefined;
for (let i = 1; i < tokens.length; i++) {
if (!tokens[i].startsWith("-")) {
subcommand = tokens[i];
break;
}
}
if (!subcommand) return next();

if (SAFE_SUBCOMMANDS.has(subcommand)) return allow();

return next();
},
};
export default allowCdk;
Loading