@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.
- package/README.md +205 -0
- package/action/action.yml +42 -0
- package/action/index.mjs +179 -0
- package/docs/state-of-agent-security-2026.md +377 -0
- package/examples/security-scan.yml +57 -0
- package/package.json +37 -0
- package/reports/aashari-mcp-server-atlassian-confluence.json +110 -0
- package/reports/aashari-mcp-server-atlassian-jira.json +138 -0
- package/reports/aashari-mcp-server-aws-sso.json +122 -0
- package/reports/agentdeskai-browser-tools-mcp.json +361 -0
- package/reports/ahmetkca-mcp-server-postgres.json +43 -0
- package/reports/aiondadotcom-mcp-ssh.json +166 -0
- package/reports/apify-actors-mcp-server.json +43 -0
- package/reports/azure-mcp.json +43 -0
- package/reports/boilerplate-mcp-tool.json +43 -0
- package/reports/browserstack-mcp-server.json +43 -0
- package/reports/canvas-mcp-server.json +43 -0
- package/reports/canvas-mcp-tool.json +43 -0
- package/reports/chrome-devtools-mcp.json +300 -0
- package/reports/chrome-local-mcp.json +222 -0
- package/reports/claude-flow-mcp.json +43 -0
- package/reports/cloudflare-mcp-server.json +43 -0
- package/reports/code-canvas-server.json +43 -0
- package/reports/cognitionai-metabase-mcp-server.json +43 -0
- package/reports/composio-mcp.json +43 -0
- package/reports/contentful-mcp-server.json +43 -0
- package/reports/dbhub.json +43 -0
- package/reports/desktop-commander.json +43 -0
- package/reports/dynatrace-oss-dynatrace-mcp-server.json +43 -0
- package/reports/e2b-mcp-server.json +67 -0
- package/reports/eslint-mcp.json +51 -0
- package/reports/european-parliament-mcp-server.json +1467 -0
- package/reports/exa-mcp-server.json +74 -0
- package/reports/executeautomation-playwright-mcp-server.json +418 -0
- package/reports/fast-kit-spec-kit.json +43 -0
- package/reports/felores-airtable-mcp-server.json +43 -0
- package/reports/figma-mcp.json +103 -0
- package/reports/forestadmin-mcp-server.json +43 -0
- package/reports/fullrun-mcp.json +43 -0
- package/reports/gemini-mcp-tool.json +43 -0
- package/reports/gitlab-mcp-agent-server.json +186 -0
- package/reports/grackle-ai-mcp.json +43 -0
- package/reports/heroku-mcp-server.json +333 -0
- package/reports/hisma-server-puppeteer.json +93 -0
- package/reports/hubspot-mcp-server.json +43 -0
- package/reports/hyper-mcp-shell.json +59 -0
- package/reports/iflow-mcp-server-github.json +327 -0
- package/reports/jpisnice-shadcn-ui-mcp-server.json +149 -0
- package/reports/jsonresume-mcp.json +43 -0
- package/reports/mapbox-mcp-server.json +43 -0
- package/reports/mcp-framework.json +43 -0
- package/reports/mcp-from-openapi.json +43 -0
- package/reports/mcp-handler.json +43 -0
- package/reports/mcp-proxy.json +43 -0
- package/reports/mcp-server-docker.json +59 -0
- package/reports/mcp-server-github-gist.json +108 -0
- package/reports/mcp-server-google-calendar.json +43 -0
- package/reports/mcp-server-jira-cloud.json +43 -0
- package/reports/mcp-server-kubernetes.json +43 -0
- package/reports/mcp-server-slack.json +411 -0
- package/reports/mcp-server-sqlite-npx.json +43 -0
- package/reports/mcp-server.json +43 -0
- package/reports/mcp-starter.json +59 -0
- package/reports/mcp-tool-lint.json +43 -0
- package/reports/mcporter.json +43 -0
- package/reports/mcptoolshop-mcp-tool-registry.json +43 -0
- package/reports/microsoft-devbox-mcp.json +43 -0
- package/reports/mobilenext-mobile-mcp.json +214 -0
- package/reports/modelcontextprotocol-server-brave-search.json +43 -0
- package/reports/modelcontextprotocol-server-everything.json +165 -0
- package/reports/modelcontextprotocol-server-fetch.json +43 -0
- package/reports/modelcontextprotocol-server-filesystem.json +259 -0
- package/reports/modelcontextprotocol-server-github.json +391 -0
- package/reports/modelcontextprotocol-server-memory.json +117 -0
- package/reports/modelcontextprotocol-server-postgres.json +43 -0
- package/reports/modelcontextprotocol-server-puppeteer.json +101 -0
- package/reports/modelcontextprotocol-server-sequential-thinking.json +67 -0
- package/reports/mongodb-mcp-server.json +43 -0
- package/reports/mseep-linear-mcp-server.json +43 -0
- package/reports/mseep-mcp-server-sqlite-npx.json +43 -0
- package/reports/n8n-mcp.json +123 -0
- package/reports/notepost-mcp.json +43 -0
- package/reports/notionhq-notion-mcp-server.json +220 -0
- package/reports/nx-mcp.json +59 -0
- package/reports/obsidian-mcp-server.json +43 -0
- package/reports/opengraph-io-mcp.json +130 -0
- package/reports/payloadcms-plugin-mcp.json +43 -0
- package/reports/peac-mappings-mcp.json +43 -0
- package/reports/playwright-mcp.json +236 -0
- package/reports/puppeteer-mcp-server.json +43 -0
- package/reports/railway-mcp-server.json +194 -0
- package/reports/razorpay-blade-mcp.json +182 -0
- package/reports/rekog-mcp-nest.json +43 -0
- package/reports/remotion-mcp.json +51 -0
- package/reports/rollbar-mcp-server.json +43 -0
- package/reports/sap-ux-fiori-mcp-server.json +80 -0
- package/reports/sentry-mcp-server.json +43 -0
- package/reports/server-filesystem.json +43 -0
- package/reports/server-memory.json +43 -0
- package/reports/shortcut-mcp.json +43 -0
- package/reports/supabase-mcp-server-supabase.json +43 -0
- package/reports/tavily-mcp.json +79 -0
- package/reports/thelord-mcp-server-docker-npx.json +43 -0
- package/reports/tyk-technologies-api-to-mcp.json +43 -0
- package/reports/tyk-technologies-tyk-dashboard-mcp.json +43 -0
- package/reports/ui5-mcp-server.json +157 -0
- package/reports/upstash-context7-mcp.json +82 -0
- package/reports/vantasdk-vanta-mcp-server.json +43 -0
- package/reports/winor30-mcp-server-datadog.json +43 -0
- package/reports/wonderwhy-er-desktop-commander.json +43 -0
- package/reports/xzxzzx-bilibili-mcp.json +58 -0
- package/src/grader.mjs +66 -0
- package/src/index.mjs +108 -0
- package/src/reporter.mjs +158 -0
- package/src/rules.mjs +363 -0
- 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'
|
package/action/index.mjs
ADDED
|
@@ -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();
|