@agentsid/scanner 0.1.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.
Files changed (116) hide show
  1. package/README.md +205 -0
  2. package/action/action.yml +42 -0
  3. package/action/index.mjs +179 -0
  4. package/docs/state-of-agent-security-2026.md +377 -0
  5. package/examples/security-scan.yml +57 -0
  6. package/package.json +37 -0
  7. package/reports/aashari-mcp-server-atlassian-confluence.json +110 -0
  8. package/reports/aashari-mcp-server-atlassian-jira.json +138 -0
  9. package/reports/aashari-mcp-server-aws-sso.json +122 -0
  10. package/reports/agentdeskai-browser-tools-mcp.json +361 -0
  11. package/reports/ahmetkca-mcp-server-postgres.json +43 -0
  12. package/reports/aiondadotcom-mcp-ssh.json +166 -0
  13. package/reports/apify-actors-mcp-server.json +43 -0
  14. package/reports/azure-mcp.json +43 -0
  15. package/reports/boilerplate-mcp-tool.json +43 -0
  16. package/reports/browserstack-mcp-server.json +43 -0
  17. package/reports/canvas-mcp-server.json +43 -0
  18. package/reports/canvas-mcp-tool.json +43 -0
  19. package/reports/chrome-devtools-mcp.json +300 -0
  20. package/reports/chrome-local-mcp.json +222 -0
  21. package/reports/claude-flow-mcp.json +43 -0
  22. package/reports/cloudflare-mcp-server.json +43 -0
  23. package/reports/code-canvas-server.json +43 -0
  24. package/reports/cognitionai-metabase-mcp-server.json +43 -0
  25. package/reports/composio-mcp.json +43 -0
  26. package/reports/contentful-mcp-server.json +43 -0
  27. package/reports/dbhub.json +43 -0
  28. package/reports/desktop-commander.json +43 -0
  29. package/reports/dynatrace-oss-dynatrace-mcp-server.json +43 -0
  30. package/reports/e2b-mcp-server.json +67 -0
  31. package/reports/eslint-mcp.json +51 -0
  32. package/reports/european-parliament-mcp-server.json +1467 -0
  33. package/reports/exa-mcp-server.json +74 -0
  34. package/reports/executeautomation-playwright-mcp-server.json +418 -0
  35. package/reports/fast-kit-spec-kit.json +43 -0
  36. package/reports/felores-airtable-mcp-server.json +43 -0
  37. package/reports/figma-mcp.json +103 -0
  38. package/reports/forestadmin-mcp-server.json +43 -0
  39. package/reports/fullrun-mcp.json +43 -0
  40. package/reports/gemini-mcp-tool.json +43 -0
  41. package/reports/gitlab-mcp-agent-server.json +186 -0
  42. package/reports/grackle-ai-mcp.json +43 -0
  43. package/reports/heroku-mcp-server.json +333 -0
  44. package/reports/hisma-server-puppeteer.json +93 -0
  45. package/reports/hubspot-mcp-server.json +43 -0
  46. package/reports/hyper-mcp-shell.json +59 -0
  47. package/reports/iflow-mcp-server-github.json +327 -0
  48. package/reports/jpisnice-shadcn-ui-mcp-server.json +149 -0
  49. package/reports/jsonresume-mcp.json +43 -0
  50. package/reports/mapbox-mcp-server.json +43 -0
  51. package/reports/mcp-framework.json +43 -0
  52. package/reports/mcp-from-openapi.json +43 -0
  53. package/reports/mcp-handler.json +43 -0
  54. package/reports/mcp-proxy.json +43 -0
  55. package/reports/mcp-server-docker.json +59 -0
  56. package/reports/mcp-server-github-gist.json +108 -0
  57. package/reports/mcp-server-google-calendar.json +43 -0
  58. package/reports/mcp-server-jira-cloud.json +43 -0
  59. package/reports/mcp-server-kubernetes.json +43 -0
  60. package/reports/mcp-server-slack.json +411 -0
  61. package/reports/mcp-server-sqlite-npx.json +43 -0
  62. package/reports/mcp-server.json +43 -0
  63. package/reports/mcp-starter.json +59 -0
  64. package/reports/mcp-tool-lint.json +43 -0
  65. package/reports/mcporter.json +43 -0
  66. package/reports/mcptoolshop-mcp-tool-registry.json +43 -0
  67. package/reports/microsoft-devbox-mcp.json +43 -0
  68. package/reports/mobilenext-mobile-mcp.json +214 -0
  69. package/reports/modelcontextprotocol-server-brave-search.json +43 -0
  70. package/reports/modelcontextprotocol-server-everything.json +165 -0
  71. package/reports/modelcontextprotocol-server-fetch.json +43 -0
  72. package/reports/modelcontextprotocol-server-filesystem.json +259 -0
  73. package/reports/modelcontextprotocol-server-github.json +391 -0
  74. package/reports/modelcontextprotocol-server-memory.json +117 -0
  75. package/reports/modelcontextprotocol-server-postgres.json +43 -0
  76. package/reports/modelcontextprotocol-server-puppeteer.json +101 -0
  77. package/reports/modelcontextprotocol-server-sequential-thinking.json +67 -0
  78. package/reports/mongodb-mcp-server.json +43 -0
  79. package/reports/mseep-linear-mcp-server.json +43 -0
  80. package/reports/mseep-mcp-server-sqlite-npx.json +43 -0
  81. package/reports/n8n-mcp.json +123 -0
  82. package/reports/notepost-mcp.json +43 -0
  83. package/reports/notionhq-notion-mcp-server.json +220 -0
  84. package/reports/nx-mcp.json +59 -0
  85. package/reports/obsidian-mcp-server.json +43 -0
  86. package/reports/opengraph-io-mcp.json +130 -0
  87. package/reports/payloadcms-plugin-mcp.json +43 -0
  88. package/reports/peac-mappings-mcp.json +43 -0
  89. package/reports/playwright-mcp.json +236 -0
  90. package/reports/puppeteer-mcp-server.json +43 -0
  91. package/reports/railway-mcp-server.json +194 -0
  92. package/reports/razorpay-blade-mcp.json +182 -0
  93. package/reports/rekog-mcp-nest.json +43 -0
  94. package/reports/remotion-mcp.json +51 -0
  95. package/reports/rollbar-mcp-server.json +43 -0
  96. package/reports/sap-ux-fiori-mcp-server.json +80 -0
  97. package/reports/sentry-mcp-server.json +43 -0
  98. package/reports/server-filesystem.json +43 -0
  99. package/reports/server-memory.json +43 -0
  100. package/reports/shortcut-mcp.json +43 -0
  101. package/reports/supabase-mcp-server-supabase.json +43 -0
  102. package/reports/tavily-mcp.json +79 -0
  103. package/reports/thelord-mcp-server-docker-npx.json +43 -0
  104. package/reports/tyk-technologies-api-to-mcp.json +43 -0
  105. package/reports/tyk-technologies-tyk-dashboard-mcp.json +43 -0
  106. package/reports/ui5-mcp-server.json +157 -0
  107. package/reports/upstash-context7-mcp.json +82 -0
  108. package/reports/vantasdk-vanta-mcp-server.json +43 -0
  109. package/reports/winor30-mcp-server-datadog.json +43 -0
  110. package/reports/wonderwhy-er-desktop-commander.json +43 -0
  111. package/reports/xzxzzx-bilibili-mcp.json +58 -0
  112. package/src/grader.mjs +66 -0
  113. package/src/index.mjs +108 -0
  114. package/src/reporter.mjs +158 -0
  115. package/src/rules.mjs +363 -0
  116. package/src/scanner.mjs +208 -0
package/README.md ADDED
@@ -0,0 +1,205 @@
1
+ <p align="center">
2
+ <h1 align="center">AgentsID Scanner</h1>
3
+ <p align="center">
4
+ <strong>The Lighthouse of agent security.</strong><br/>
5
+ Scan any MCP server. Get a security report card.
6
+ </p>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://agentsid.dev"><img src="https://img.shields.io/badge/by-AgentsID-f59e0b?style=flat-square" alt="AgentsID" /></a>
11
+ <a href="https://github.com/stevenkozeniesky02/agentsid-scanner/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-f59e0b?style=flat-square" alt="License" /></a>
12
+ </p>
13
+
14
+ ---
15
+
16
+ Your MCP server exposes tools to AI agents. How secure is it?
17
+
18
+ Most MCP servers ship with no authentication, no per-tool permissions, no input validation, and tool descriptions vulnerable to prompt injection. **You just don't know it yet.**
19
+
20
+ AgentsID Scanner tells you.
21
+
22
+ ## Quick Start
23
+
24
+ ```bash
25
+ npx @agentsid/scanner -- npx @some/mcp-server
26
+ ```
27
+
28
+ That's it. You get a letter grade and detailed findings.
29
+
30
+ ## What It Scans
31
+
32
+ | Category | What It Checks | Why It Matters |
33
+ |----------|---------------|----------------|
34
+ | **Injection** | Tool descriptions for 11 prompt injection patterns | Malicious tool descriptions can hijack agent behavior |
35
+ | **Permissions** | Tool names classified by risk (destructive, execution, financial, credential) | 50 tools with no access control is a 50-surface attack |
36
+ | **Validation** | Input schemas for missing constraints, unbounded strings, optional-only params | No validation = arbitrary input to your tool handlers |
37
+ | **Auth** | Authentication indicators in tool surface | No auth tools = unauthenticated agents calling your tools |
38
+ | **Secrets** | Tools that may expose credentials in output | API keys, tokens, passwords leaked in responses |
39
+ | **Output** | Unfiltered file/data output | Sensitive file contents returned without redaction |
40
+
41
+ ## The Report
42
+
43
+ ```
44
+ ╔══════════════════════════════════════════════════════════════╗
45
+ ║ AgentsID Security Scanner — Report ║
46
+ ╚══════════════════════════════════════════════════════════════╝
47
+
48
+ Server: my-mcp-server v1.0.0
49
+ Tools: 23
50
+ Scanned: 2026-03-29T12:00:00.000Z
51
+
52
+ Overall Grade: D (42/100)
53
+
54
+ Category Grades:
55
+ injection A
56
+ permissions F
57
+ validation D
58
+ auth F
59
+ output B
60
+
61
+ Tool Risk Profile:
62
+ destructive ████ 4
63
+ execution ██ 2
64
+ credential_access █ 1
65
+
66
+ Findings: 31
67
+ CRITICAL: 2
68
+ HIGH: 8
69
+ MEDIUM: 15
70
+ LOW: 6
71
+
72
+ Recommendations:
73
+ 1. Address CRITICAL and HIGH findings immediately
74
+ 2. Add per-tool permission controls (agentsid.dev/docs)
75
+ 3. Implement input validation on all tool parameters
76
+ 4. Add authentication to server endpoints
77
+ ```
78
+
79
+ ## Usage
80
+
81
+ ### Scan a local MCP server (stdio)
82
+
83
+ ```bash
84
+ # Scan any npx-installable MCP server
85
+ agentsid-scan -- npx @modelcontextprotocol/server-filesystem ./
86
+
87
+ # Scan a local server file
88
+ agentsid-scan -- node my-server.mjs
89
+
90
+ # Scan a Python MCP server
91
+ agentsid-scan -- python -m my_mcp_server
92
+ ```
93
+
94
+ ### Scan a remote MCP server (HTTP)
95
+
96
+ ```bash
97
+ agentsid-scan --url https://mcp.example.com/mcp
98
+ ```
99
+
100
+ ### JSON output
101
+
102
+ ```bash
103
+ agentsid-scan --json -- npx @some/mcp-server > report.json
104
+ ```
105
+
106
+ ### Pass environment variables
107
+
108
+ ```bash
109
+ agentsid-scan --env API_KEY=xxx --env DB_URL=postgres://... -- node server.mjs
110
+ ```
111
+
112
+ ## Grading
113
+
114
+ Starts at 100 points. Deductions per finding:
115
+
116
+ | Severity | Deduction | Example |
117
+ |----------|-----------|---------|
118
+ | CRITICAL | -25 | Shell execution tool with no auth |
119
+ | HIGH | -15 | Tool exposes credentials in output |
120
+ | MEDIUM | -8 | String params without length limits |
121
+ | LOW | -3 | Optional-only input parameters |
122
+ | INFO | 0 | Read-only tool detected |
123
+
124
+ | Grade | Score | Meaning |
125
+ |-------|-------|---------|
126
+ | A | 90-100 | Excellent security posture |
127
+ | B | 75-89 | Good — minor issues |
128
+ | C | 60-74 | Acceptable — needs improvement |
129
+ | D | 40-59 | Poor — significant risks |
130
+ | F | 0-39 | Failing — critical vulnerabilities |
131
+
132
+ ## Injection Detection
133
+
134
+ The scanner checks tool descriptions for 11 prompt injection patterns:
135
+
136
+ - **Instruction override** — "ignore previous instructions", "disregard all rules"
137
+ - **Role hijacking** — "you are now a..."
138
+ - **Memory wipe** — "forget everything"
139
+ - **Tool redirection** — "instead of X, call Y"
140
+ - **Hidden actions** — "also execute..."
141
+ - **Concealment** — "do not tell the user"
142
+ - **Stealth operations** — "secretly", "covertly"
143
+ - **Security bypass** — "override auth", "skip validation"
144
+ - **Encoded payloads** — base64, eval(), template injections
145
+ - **Unicode obfuscation** — escaped characters hiding instructions
146
+
147
+ ## Risk Classification
148
+
149
+ Every tool is classified by name pattern:
150
+
151
+ | Risk Level | Patterns | Example Tools |
152
+ |------------|----------|---------------|
153
+ | **Critical** | execute, shell, admin, sudo, payment | `shell_run`, `admin_reset`, `process_payment` |
154
+ | **High** | delete, remove, drop, deploy, credential | `delete_user`, `deploy_prod`, `get_api_key` |
155
+ | **Medium** | create, update, send, write | `create_issue`, `send_email`, `write_file` |
156
+ | **Info** | read, get, list, search, describe | `get_status`, `list_users`, `search_docs` |
157
+
158
+ ## Fix Your Grade
159
+
160
+ The scanner tells you what's wrong. Here's how to fix it:
161
+
162
+ ### Add per-tool permissions
163
+
164
+ ```bash
165
+ npm install @agentsid/guard
166
+ ```
167
+
168
+ [AgentsID Guard](https://github.com/stevenkozeniesky02/shell-guard) validates every tool call against permission rules before execution. 50 tools, 16 categories, all protected.
169
+
170
+ ### Or add the SDK to your existing server
171
+
172
+ ```bash
173
+ npm install @agentsid/sdk
174
+ ```
175
+
176
+ Three lines of middleware in your MCP server. Full docs at [agentsid.dev/docs](https://agentsid.dev/docs).
177
+
178
+ ## Programmatic Usage
179
+
180
+ ```javascript
181
+ import { scanStdio, scanHttp, scanToolDefinitions } from "@agentsid/scanner";
182
+
183
+ // Scan a local server
184
+ const report = await scanStdio("npx @some/server", { json: true });
185
+
186
+ // Scan a remote server
187
+ const report = await scanHttp("https://mcp.example.com", { json: true });
188
+
189
+ // Scan tool definitions directly (no server needed)
190
+ const report = scanToolDefinitions(myToolArray, { json: true });
191
+ ```
192
+
193
+ ## Contributing
194
+
195
+ Found a pattern we're not detecting? Open an issue or PR. The rule engine is in `src/rules.mjs` — adding a new pattern is one regex.
196
+
197
+ ## Links
198
+
199
+ - [AgentsID](https://agentsid.dev) — Identity & auth for AI agents
200
+ - [AgentsID Guard](https://github.com/stevenkozeniesky02/shell-guard) — 50-tool protected MCP server
201
+ - [Documentation](https://agentsid.dev/docs)
202
+
203
+ ## License
204
+
205
+ MIT
@@ -0,0 +1,42 @@
1
+ name: 'AgentsID Security Scanner'
2
+ description: 'Scan MCP server tool definitions for security vulnerabilities. Grades auth, permissions, injection risks, and tool safety.'
3
+ author: 'AgentsID'
4
+
5
+ branding:
6
+ icon: 'shield'
7
+ color: 'orange'
8
+
9
+ inputs:
10
+ server-command:
11
+ description: 'Command to start the MCP server (stdio transport)'
12
+ required: false
13
+ server-url:
14
+ description: 'URL of a remote MCP server (HTTP transport)'
15
+ required: false
16
+ tool-definitions:
17
+ description: 'Path to a JSON file containing tool definitions (static analysis, no server needed)'
18
+ required: false
19
+ fail-on:
20
+ description: 'Fail the check if grade is below this (A, B, C, D, F). Default: no failure.'
21
+ required: false
22
+ default: ''
23
+ comment:
24
+ description: 'Post results as a PR comment (true/false). Default: true'
25
+ required: false
26
+ default: 'true'
27
+
28
+ outputs:
29
+ grade:
30
+ description: 'Overall letter grade (A-F)'
31
+ score:
32
+ description: 'Numeric score (0-100)'
33
+ findings:
34
+ description: 'Number of findings'
35
+ critical:
36
+ description: 'Number of critical findings'
37
+ report:
38
+ description: 'Full JSON report'
39
+
40
+ runs:
41
+ using: 'node20'
42
+ main: 'index.mjs'
@@ -0,0 +1,179 @@
1
+ /**
2
+ * AgentsID Security Scanner — GitHub Action
3
+ *
4
+ * Runs on every PR to an MCP server repo. Scans tool definitions,
5
+ * grades security posture, and comments on the PR with findings.
6
+ *
7
+ * Three scan modes:
8
+ * 1. server-command: Start an MCP server via stdio and enumerate tools
9
+ * 2. server-url: Connect to a remote MCP server via HTTP
10
+ * 3. tool-definitions: Static analysis of a JSON file with tool definitions
11
+ */
12
+
13
+ import * as core from "@actions/core";
14
+ import * as github from "@actions/github";
15
+ import { scanStdio, scanHttp, scanToolDefinitions } from "../src/scanner.mjs";
16
+ import { readFileSync } from "fs";
17
+
18
+ async function run() {
19
+ try {
20
+ const serverCommand = core.getInput("server-command");
21
+ const serverUrl = core.getInput("server-url");
22
+ const toolDefsPath = core.getInput("tool-definitions");
23
+ const failOn = core.getInput("fail-on").toUpperCase();
24
+ const shouldComment = core.getInput("comment") === "true";
25
+
26
+ let reportJson;
27
+
28
+ // ── Scan ──
29
+ if (serverCommand) {
30
+ core.info(`Scanning MCP server: ${serverCommand}`);
31
+ reportJson = await scanStdio(serverCommand, { json: true, timeout: 60000 });
32
+ } else if (serverUrl) {
33
+ core.info(`Scanning remote MCP server: ${serverUrl}`);
34
+ reportJson = await scanHttp(serverUrl, { json: true, timeout: 30000 });
35
+ } else if (toolDefsPath) {
36
+ core.info(`Scanning tool definitions: ${toolDefsPath}`);
37
+ const tools = JSON.parse(readFileSync(toolDefsPath, "utf-8"));
38
+ const toolArray = Array.isArray(tools) ? tools : tools.tools || [];
39
+ reportJson = scanToolDefinitions(toolArray, { json: true, serverName: toolDefsPath });
40
+ } else {
41
+ // Auto-detect: look for common MCP tool definition files
42
+ const autoFiles = [
43
+ "tools.json",
44
+ "mcp-tools.json",
45
+ "src/tools.json",
46
+ ".mcp/tools.json",
47
+ ];
48
+
49
+ let found = false;
50
+ for (const f of autoFiles) {
51
+ try {
52
+ const content = readFileSync(f, "utf-8");
53
+ const tools = JSON.parse(content);
54
+ const toolArray = Array.isArray(tools) ? tools : tools.tools || [];
55
+ if (toolArray.length > 0) {
56
+ core.info(`Auto-detected tool definitions: ${f}`);
57
+ reportJson = scanToolDefinitions(toolArray, { json: true, serverName: f });
58
+ found = true;
59
+ break;
60
+ }
61
+ } catch {}
62
+ }
63
+
64
+ if (!found) {
65
+ core.warning("No scan target specified. Use server-command, server-url, or tool-definitions input.");
66
+ return;
67
+ }
68
+ }
69
+
70
+ const report = JSON.parse(reportJson);
71
+
72
+ // ── Set Outputs ──
73
+ core.setOutput("grade", report.grade.overall);
74
+ core.setOutput("score", report.grade.score);
75
+ core.setOutput("findings", report.findings.length);
76
+ core.setOutput("critical", report.summary.CRITICAL || 0);
77
+ core.setOutput("report", reportJson);
78
+
79
+ // ── Log Summary ──
80
+ core.info(`Grade: ${report.grade.overall} (${report.grade.score}/100)`);
81
+ core.info(`Tools: ${report.toolCount}`);
82
+ core.info(`Findings: ${report.findings.length} (${report.summary.CRITICAL || 0} critical, ${report.summary.HIGH || 0} high)`);
83
+
84
+ // ── PR Comment ──
85
+ if (shouldComment && github.context.payload.pull_request) {
86
+ const token = process.env.GITHUB_TOKEN;
87
+ if (token) {
88
+ const octokit = github.getOctokit(token);
89
+ const { owner, repo } = github.context.repo;
90
+ const prNumber = github.context.payload.pull_request.number;
91
+
92
+ const gradeEmoji = {
93
+ A: "🟢", B: "🟢", C: "🟡", D: "🔴", F: "🔴"
94
+ }[report.grade.overall] || "⚪";
95
+
96
+ // Build category grades table
97
+ const catRows = Object.entries(report.grade.categories || {})
98
+ .map(([cat, g]) => `| ${cat} | ${g} |`)
99
+ .join("\n");
100
+
101
+ // Build critical/high findings list
102
+ const criticalFindings = report.findings
103
+ .filter((f) => f.severity === "CRITICAL" || f.severity === "HIGH")
104
+ .slice(0, 10)
105
+ .map((f) => `- **${f.severity}**: ${f.detail}${f.tool && f.tool !== "*" ? ` (\`${f.tool}\`)` : ""}`)
106
+ .join("\n");
107
+
108
+ const commentBody = `## ${gradeEmoji} AgentsID Security Scan: **${report.grade.overall}** (${report.grade.score}/100)
109
+
110
+ | Metric | Value |
111
+ |--------|-------|
112
+ | Tools scanned | ${report.toolCount} |
113
+ | Total findings | ${report.findings.length} |
114
+ | Critical | ${report.summary.CRITICAL || 0} |
115
+ | High | ${report.summary.HIGH || 0} |
116
+ | Medium | ${report.summary.MEDIUM || 0} |
117
+ | Low | ${report.summary.LOW || 0} |
118
+
119
+ ### Category Grades
120
+
121
+ | Category | Grade |
122
+ |----------|-------|
123
+ ${catRows}
124
+
125
+ ${criticalFindings ? `### Critical & High Findings\n\n${criticalFindings}` : "### No critical or high findings 🎉"}
126
+
127
+ ${report.grade.score < 60 ? `### Recommendations
128
+
129
+ 1. Add per-tool permission controls — [AgentsID Docs](https://agentsid.dev/docs)
130
+ 2. Implement input validation on tool parameters
131
+ 3. Add authentication to server endpoints
132
+ 4. Use [AgentsID Guard](https://github.com/stevenkozeniesky02/shell-guard) for built-in protection` : ""}
133
+
134
+ ---
135
+ <sub>Scanned by [AgentsID Security Scanner](https://github.com/stevenkozeniesky02/agentsid-scanner) · [Fix your grade](https://agentsid.dev/docs)</sub>`;
136
+
137
+ // Check for existing comment and update it
138
+ const { data: comments } = await octokit.rest.issues.listComments({
139
+ owner, repo, issue_number: prNumber,
140
+ });
141
+
142
+ const existingComment = comments.find(
143
+ (c) => c.body?.includes("AgentsID Security Scan")
144
+ );
145
+
146
+ if (existingComment) {
147
+ await octokit.rest.issues.updateComment({
148
+ owner, repo, comment_id: existingComment.id, body: commentBody,
149
+ });
150
+ core.info("Updated existing PR comment");
151
+ } else {
152
+ await octokit.rest.issues.createComment({
153
+ owner, repo, issue_number: prNumber, body: commentBody,
154
+ });
155
+ core.info("Posted PR comment");
156
+ }
157
+ } else {
158
+ core.warning("GITHUB_TOKEN not set — cannot post PR comment");
159
+ }
160
+ }
161
+
162
+ // ── Fail Check ──
163
+ if (failOn) {
164
+ const gradeOrder = { A: 5, B: 4, C: 3, D: 2, F: 1 };
165
+ const currentGrade = gradeOrder[report.grade.overall] || 0;
166
+ const requiredGrade = gradeOrder[failOn] || 0;
167
+
168
+ if (currentGrade < requiredGrade) {
169
+ core.setFailed(
170
+ `Security grade ${report.grade.overall} (${report.grade.score}/100) is below required grade ${failOn}`
171
+ );
172
+ }
173
+ }
174
+ } catch (err) {
175
+ core.setFailed(`Scanner failed: ${err.message}`);
176
+ }
177
+ }
178
+
179
+ run();