From 4d94cf1fca8049f38659fa38861f8310e26538b8 Mon Sep 17 00:00:00 2001 From: "Sharad." Date: Sat, 12 Sep 2026 14:09:03 +0000 Subject: [PATCH 1/2] Fix --only "" running all rules instead of none Treat an explicit --only flag (including an empty value) as a rule allowlist. An empty allowlist runs zero rules. Distinguish that from the default config where enabledRules is unset for filtering. Fixes #26. Co-authored-by: Sharad. --- package-lock.json | 10 +++++----- src/audit.ts | 4 +++- src/cli.ts | 7 +++++-- src/config.ts | 7 +++++++ src/engine/engine.ts | 2 +- test/cli.test.ts | 12 +++++++++++- test/engine.test.ts | 7 +++++++ 7 files changed, 39 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11d4ce8..bcc4295 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "mcp-audit", - "version": "0.1.0", + "name": "@royalpinto007/mcp-audit", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "mcp-audit", - "version": "0.1.0", + "name": "@royalpinto007/mcp-audit", + "version": "0.1.1", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.0.4", @@ -21,7 +21,7 @@ "vitest": "^2.1.8" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/@esbuild/aix-ppc64": { diff --git a/src/audit.ts b/src/audit.ts index 3cceb10..e661faf 100644 --- a/src/audit.ts +++ b/src/audit.ts @@ -38,9 +38,11 @@ export function runAudit( rules: Rule[] = ALL_RULES, ): AuditResult { const engine = new Engine(rules); + const useAllowlist = + config.enforceEnabledRules || config.enabledRules.length > 0; const raw = engine.run(target, { disabledRules: config.disabledRules, - enabledRules: config.enabledRules, + enabledRules: useAllowlist ? config.enabledRules : undefined, severityOverrides: config.severityOverrides, }); const findings = applyIgnores(raw.findings, config.ignore); diff --git a/src/cli.ts b/src/cli.ts index c9e8c4e..a645900 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -92,7 +92,7 @@ function csv(value: CliFlag | undefined): string[] { .filter(Boolean); } -function overlayFlags( +export function overlayFlags( base: McpAuditConfig, flags: Record, ): McpAuditConfig { @@ -107,7 +107,10 @@ function overlayFlags( overlay.failOn = fo; } if (flags["disable"]) overlay.disabledRules = csv(flags["disable"]); - if (flags["only"]) overlay.enabledRules = csv(flags["only"]); + if (flags["only"] !== undefined) { + overlay.enabledRules = csv(flags["only"]); + overlay.enforceEnabledRules = true; + } return normalizeConfig(overlay, base); } diff --git a/src/config.ts b/src/config.ts index 5a8d4c2..7e44f20 100644 --- a/src/config.ts +++ b/src/config.ts @@ -10,6 +10,8 @@ export interface McpAuditConfig { disabledRules: string[]; /** If non-empty, only these rule ids run. */ enabledRules: string[]; + /** When true, enabledRules is an allowlist (including empty → no rules). */ + enforceEnabledRules: boolean; /** Per-rule severity overrides. */ severityOverrides: Record; /** Findings at or above this severity cause a non-zero exit. */ @@ -21,6 +23,7 @@ export interface McpAuditConfig { export const DEFAULT_CONFIG: McpAuditConfig = { disabledRules: [], enabledRules: [], + enforceEnabledRules: false, severityOverrides: {}, failOn: "high", ignore: [], @@ -65,6 +68,7 @@ export function normalizeConfig( const config: McpAuditConfig = { disabledRules: [...base.disabledRules], enabledRules: [...base.enabledRules], + enforceEnabledRules: base.enforceEnabledRules, severityOverrides: { ...base.severityOverrides }, failOn: base.failOn, ignore: [...base.ignore], @@ -76,6 +80,9 @@ export function normalizeConfig( if (Array.isArray(obj.enabledRules)) { config.enabledRules = obj.enabledRules.map(String); } + if (obj.enforceEnabledRules === true) { + config.enforceEnabledRules = true; + } if (Array.isArray(obj.ignore)) { config.ignore = obj.ignore.map(String); } diff --git a/src/engine/engine.ts b/src/engine/engine.ts index 29047e5..f48ca04 100644 --- a/src/engine/engine.ts +++ b/src/engine/engine.ts @@ -41,7 +41,7 @@ export class Engine { activeRules(options: EngineOptions = {}): Rule[] { const { disabledRules = [], enabledRules } = options; return this.rules.filter((rule) => { - if (enabledRules && enabledRules.length > 0) { + if (enabledRules !== undefined) { return enabledRules.includes(rule.id); } return !disabledRules.includes(rule.id); diff --git a/test/cli.test.ts b/test/cli.test.ts index 902e195..c8a8ea5 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -1,5 +1,8 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { collectHeaders, main, parseArgs } from "../src/cli.js"; +import { collectHeaders, main, overlayFlags, parseArgs } from "../src/cli.js"; +import { DEFAULT_CONFIG } from "../src/config.js"; +import { runAudit } from "../src/audit.js"; +import { makeTarget } from "./helpers.js"; afterEach(() => { vi.restoreAllMocks(); @@ -58,6 +61,13 @@ describe("HTTP headers", () => { expect(collectHeaders(flags)).toEqual({ Authorization: "Bearer a:b" }); }); + it("treats --only \"\" as an empty allowlist (zero rules run)", () => { + const { flags } = parseArgs(["static", "manifest.json", "--only", ""]); + const config = overlayFlags(DEFAULT_CONFIG, flags); + const result = runAudit(makeTarget(), config); + expect(result.rulesRun).toEqual([]); + }); + it("keeps last-wins behavior for repeated non-header flags", () => { const { flags } = parseArgs([ "http", diff --git a/test/engine.test.ts b/test/engine.test.ts index 1f69ee5..06d542d 100644 --- a/test/engine.test.ts +++ b/test/engine.test.ts @@ -40,6 +40,13 @@ describe("Engine", () => { expect(result.findings.map((f) => f.ruleId)).toEqual(["A"]); }); + it("runs no rules when enabledRules is an empty allowlist", () => { + const engine = new Engine([rule("A", "low"), rule("B", "high")]); + const result = engine.run(makeTarget(), { enabledRules: [] }); + expect(result.rulesRun).toEqual([]); + expect(result.findings).toEqual([]); + }); + it("applies severity overrides", () => { const engine = new Engine([rule("A", "low")]); const result = engine.run(makeTarget(), { From 69d64544c6d71ac3af5f4bdde2a6d1ae84ee0842 Mon Sep 17 00:00:00 2001 From: royalpinto007 Date: Sat, 12 Sep 2026 22:21:50 +0530 Subject: [PATCH 2/2] drop unrelated lock rename, allow enforceEnabledRules in config files --- package-lock.json | 10 +++++----- src/config.ts | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index bcc4295..11d4ce8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "@royalpinto007/mcp-audit", - "version": "0.1.1", + "name": "mcp-audit", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@royalpinto007/mcp-audit", - "version": "0.1.1", + "name": "mcp-audit", + "version": "0.1.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.0.4", @@ -21,7 +21,7 @@ "vitest": "^2.1.8" }, "engines": { - "node": ">=20" + "node": ">=18" } }, "node_modules/@esbuild/aix-ppc64": { diff --git a/src/config.ts b/src/config.ts index e53402d..0974a75 100644 --- a/src/config.ts +++ b/src/config.ts @@ -32,6 +32,7 @@ export const DEFAULT_CONFIG: McpAuditConfig = { const KNOWN_CONFIG_KEYS = new Set([ "disabledRules", "enabledRules", + "enforceEnabledRules", "severityOverrides", "failOn", "ignore",