git clone https://github.com/affaan-m/agentshield.git
cd agentshield
npm install
npm run typecheck
npm run scan:demo # verify everything worksRequires Node >= 20.
npm run dev scan --path examples/vulnerable # run scanner in dev mode
npm run build # build with tsup
npm run typecheck # type check
npm test # run tests
npm run test:coverage # tests with coverageTests use Vitest and live in tests/. Convention:
tests/
βββ rules/
β βββ secrets.test.ts
β βββ mcp.test.ts
β βββ hooks.test.ts
β βββ permissions.test.ts
β βββ agents.test.ts
βββ scanner/
β βββ discovery.test.ts
βββ reporter/
βββ score.test.ts
Each test file mirrors its source counterpart. Run a single file with:
npx vitest tests/rules/secrets.test.tsRules live in src/rules/. Each rule implements the Rule interface from src/types.ts:
import type { ConfigFile, Finding, Rule } from "../types.js";
export const myRules: ReadonlyArray<Rule> = [
{
id: "category-short-name",
name: "Human-Readable Name",
description: "What this rule checks for",
severity: "high", // critical | high | medium | low | info
category: "permissions", // secrets | permissions | hooks | mcp | agents | injection | exposure | misconfiguration
check(file: ConfigFile): ReadonlyArray<Finding> {
const findings: Finding[] = [];
// Your detection logic here.
// file.content is the raw file text.
// file.type tells you what kind of config it is.
// Return findings with evidence and optional fix suggestions.
return findings;
},
},
];Then register your rules in src/rules/index.ts:
import { myRules } from "./my-rules.js";
export function getBuiltinRules(): ReadonlyArray<Rule> {
return [
...secretRules,
...permissionRules,
...hookRules,
...mcpRules,
...agentRules,
...myRules, // add here
];
}Add a vulnerable example in examples/vulnerable/ that triggers your rule, and write a test in tests/rules/.
If your rule can suggest a fix, include a Fix object:
fix: {
description: "What the fix does",
before: "the problematic text",
after: "the safe replacement",
auto: true, // true = safe to apply with --fix, false = manual review needed
}Only set auto: true if the fix is safe to apply without human review.
- Immutable by default β use
readonlyon all interface fields andReadonlyArray - Pure functions where possible β rules are
(ConfigFile) => Finding[] - No mutation β create new objects, don't modify existing ones
- TypeScript strict mode β no
any, no implicit returns
- Fork the repo and create a branch from
main - Add your rule + test + vulnerable example
- Run
npm run typecheck && npm test && npm run scan:demo - Open a PR with a clear description of what the rule catches and why it matters
By contributing, you agree that your contributions will be licensed under the MIT License.