@authora/agent-audit 0.1.0 → 0.1.1
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/dist/scanner.js +32 -13
- package/package.json +1 -1
- package/src/scanner.ts +34 -13
package/dist/scanner.js
CHANGED
|
@@ -54,18 +54,33 @@ export async function scanDirectory(dir) {
|
|
|
54
54
|
let hasAuditLog = false;
|
|
55
55
|
let hasApprovals = false;
|
|
56
56
|
const files = walkDir(dir);
|
|
57
|
-
|
|
57
|
+
// Skip non-backend files (frontend components, tests, docs, configs)
|
|
58
|
+
const SKIP_PATTERNS = [
|
|
59
|
+
/\.(test|spec|stories)\.[tj]sx?$/,
|
|
60
|
+
/\/__(tests|mocks|fixtures)__\//,
|
|
61
|
+
/\/(web|ui|frontend|app)\/src\/(components|pages|hooks|stores|lib)\//,
|
|
62
|
+
/\.d\.ts$/,
|
|
63
|
+
/\.(md|mdx|txt|svg|css|scss|html)$/,
|
|
64
|
+
/package\.json$/,
|
|
65
|
+
/package-lock\.json$/,
|
|
66
|
+
/tsconfig.*\.json$/,
|
|
67
|
+
];
|
|
68
|
+
const backendFiles = files.filter((f) => {
|
|
69
|
+
const rel = f.replace(dir + "/", "");
|
|
70
|
+
return !SKIP_PATTERNS.some((p) => p.test(rel));
|
|
71
|
+
});
|
|
72
|
+
for (const file of backendFiles) {
|
|
58
73
|
const content = readFile(file);
|
|
59
74
|
if (!content)
|
|
60
75
|
continue;
|
|
61
76
|
const lower = content.toLowerCase();
|
|
62
77
|
const relPath = file.replace(dir + "/", "");
|
|
63
|
-
// Detect agents
|
|
64
|
-
if (/\bagent\b/i.test(content) && (/\
|
|
78
|
+
// Detect agents -- only in backend code with real agent patterns
|
|
79
|
+
if (/\bagent\b/i.test(content) && (/\bcreateAgent|AgentConfig|new.*Agent\(|runAgent|agentWorkflow\b/i.test(content))) {
|
|
65
80
|
agents++;
|
|
66
81
|
}
|
|
67
|
-
// Detect MCP servers
|
|
68
|
-
if (/
|
|
82
|
+
// Detect MCP servers -- only files that actually create/configure MCP servers
|
|
83
|
+
if (/new.*Server|createServer|McpServer|startServer/i.test(content) && /mcp|model.*context.*protocol/i.test(lower)) {
|
|
69
84
|
mcpServers++;
|
|
70
85
|
}
|
|
71
86
|
// Check for identity layer
|
|
@@ -120,27 +135,30 @@ export async function scanDirectory(dir) {
|
|
|
120
135
|
}
|
|
121
136
|
}
|
|
122
137
|
// --- WARNING: MCP server without auth ---
|
|
123
|
-
|
|
138
|
+
// Only flag files that actually create/start MCP servers, not type defs or configs
|
|
139
|
+
if (/new.*Server|createServer|McpServer/i.test(content) && /mcp/i.test(lower) && !/auth|authentication|authorization|token|apiKey|signature/i.test(content)) {
|
|
124
140
|
findings.push({
|
|
125
141
|
severity: "warning",
|
|
126
142
|
category: "mcp",
|
|
127
|
-
message: "MCP server
|
|
143
|
+
message: "MCP server created without visible auth configuration",
|
|
128
144
|
file: relPath,
|
|
129
145
|
fix: "Add authentication to your MCP server (API key, JWT, or Ed25519 signature verification)",
|
|
130
146
|
});
|
|
131
147
|
}
|
|
132
148
|
// --- WARNING: Broad agent permissions ---
|
|
133
|
-
if
|
|
149
|
+
// Only flag if it's clearly giving an agent admin/wildcard access
|
|
150
|
+
if (/permissions.*\[.*["']\*["']|role.*["']admin["']|full.*access.*agent/i.test(content)) {
|
|
134
151
|
findings.push({
|
|
135
152
|
severity: "warning",
|
|
136
153
|
category: "permissions",
|
|
137
|
-
message: "Agent
|
|
154
|
+
message: "Agent configured with overly broad permissions",
|
|
138
155
|
file: relPath,
|
|
139
156
|
fix: "Apply least-privilege: give agents only the permissions they need for their specific task",
|
|
140
157
|
});
|
|
141
158
|
}
|
|
142
159
|
// --- WARNING: No error handling on tool calls ---
|
|
143
|
-
|
|
160
|
+
// Only flag actual tool execution code, not type definitions or prompts
|
|
161
|
+
if (/callTool\(|executeTool\(|tool\.execute\(/i.test(content) && !/try\s*\{|\.catch\(/i.test(content)) {
|
|
144
162
|
findings.push({
|
|
145
163
|
severity: "warning",
|
|
146
164
|
category: "resilience",
|
|
@@ -150,11 +168,12 @@ export async function scanDirectory(dir) {
|
|
|
150
168
|
});
|
|
151
169
|
}
|
|
152
170
|
// --- INFO: Agent without timeout ---
|
|
153
|
-
|
|
171
|
+
// Only flag files with actual agent execution logic (not frontend, not configs)
|
|
172
|
+
if (/runAgent|agentWorkflow|executeAgent|startAgent/i.test(content) && /async|await/i.test(content) && !/timeout|AbortSignal|signal/i.test(content)) {
|
|
154
173
|
findings.push({
|
|
155
174
|
severity: "info",
|
|
156
175
|
category: "resilience",
|
|
157
|
-
message: "Agent
|
|
176
|
+
message: "Agent execution without timeout -- could run indefinitely",
|
|
158
177
|
file: relPath,
|
|
159
178
|
fix: "Add timeouts to agent operations to prevent runaway processes",
|
|
160
179
|
});
|
|
@@ -229,6 +248,6 @@ export async function scanDirectory(dir) {
|
|
|
229
248
|
hasDelegation,
|
|
230
249
|
hasAuditLog,
|
|
231
250
|
hasApprovals,
|
|
232
|
-
scannedFiles:
|
|
251
|
+
scannedFiles: backendFiles.length,
|
|
233
252
|
};
|
|
234
253
|
}
|
package/package.json
CHANGED
package/src/scanner.ts
CHANGED
|
@@ -79,19 +79,36 @@ export async function scanDirectory(dir: string): Promise<ScanResult> {
|
|
|
79
79
|
|
|
80
80
|
const files = walkDir(dir);
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
// Skip non-backend files (frontend components, tests, docs, configs)
|
|
83
|
+
const SKIP_PATTERNS = [
|
|
84
|
+
/\.(test|spec|stories)\.[tj]sx?$/,
|
|
85
|
+
/\/__(tests|mocks|fixtures)__\//,
|
|
86
|
+
/\/(web|ui|frontend|app)\/src\/(components|pages|hooks|stores|lib)\//,
|
|
87
|
+
/\.d\.ts$/,
|
|
88
|
+
/\.(md|mdx|txt|svg|css|scss|html)$/,
|
|
89
|
+
/package\.json$/,
|
|
90
|
+
/package-lock\.json$/,
|
|
91
|
+
/tsconfig.*\.json$/,
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const backendFiles = files.filter((f) => {
|
|
95
|
+
const rel = f.replace(dir + "/", "");
|
|
96
|
+
return !SKIP_PATTERNS.some((p) => p.test(rel));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
for (const file of backendFiles) {
|
|
83
100
|
const content = readFile(file);
|
|
84
101
|
if (!content) continue;
|
|
85
102
|
const lower = content.toLowerCase();
|
|
86
103
|
const relPath = file.replace(dir + "/", "");
|
|
87
104
|
|
|
88
|
-
// Detect agents
|
|
89
|
-
if (/\bagent\b/i.test(content) && (/\
|
|
105
|
+
// Detect agents -- only in backend code with real agent patterns
|
|
106
|
+
if (/\bagent\b/i.test(content) && (/\bcreateAgent|AgentConfig|new.*Agent\(|runAgent|agentWorkflow\b/i.test(content))) {
|
|
90
107
|
agents++;
|
|
91
108
|
}
|
|
92
109
|
|
|
93
|
-
// Detect MCP servers
|
|
94
|
-
if (/
|
|
110
|
+
// Detect MCP servers -- only files that actually create/configure MCP servers
|
|
111
|
+
if (/new.*Server|createServer|McpServer|startServer/i.test(content) && /mcp|model.*context.*protocol/i.test(lower)) {
|
|
95
112
|
mcpServers++;
|
|
96
113
|
}
|
|
97
114
|
|
|
@@ -153,29 +170,32 @@ export async function scanDirectory(dir: string): Promise<ScanResult> {
|
|
|
153
170
|
}
|
|
154
171
|
|
|
155
172
|
// --- WARNING: MCP server without auth ---
|
|
156
|
-
|
|
173
|
+
// Only flag files that actually create/start MCP servers, not type defs or configs
|
|
174
|
+
if (/new.*Server|createServer|McpServer/i.test(content) && /mcp/i.test(lower) && !/auth|authentication|authorization|token|apiKey|signature/i.test(content)) {
|
|
157
175
|
findings.push({
|
|
158
176
|
severity: "warning",
|
|
159
177
|
category: "mcp",
|
|
160
|
-
message: "MCP server
|
|
178
|
+
message: "MCP server created without visible auth configuration",
|
|
161
179
|
file: relPath,
|
|
162
180
|
fix: "Add authentication to your MCP server (API key, JWT, or Ed25519 signature verification)",
|
|
163
181
|
});
|
|
164
182
|
}
|
|
165
183
|
|
|
166
184
|
// --- WARNING: Broad agent permissions ---
|
|
167
|
-
if
|
|
185
|
+
// Only flag if it's clearly giving an agent admin/wildcard access
|
|
186
|
+
if (/permissions.*\[.*["']\*["']|role.*["']admin["']|full.*access.*agent/i.test(content)) {
|
|
168
187
|
findings.push({
|
|
169
188
|
severity: "warning",
|
|
170
189
|
category: "permissions",
|
|
171
|
-
message: "Agent
|
|
190
|
+
message: "Agent configured with overly broad permissions",
|
|
172
191
|
file: relPath,
|
|
173
192
|
fix: "Apply least-privilege: give agents only the permissions they need for their specific task",
|
|
174
193
|
});
|
|
175
194
|
}
|
|
176
195
|
|
|
177
196
|
// --- WARNING: No error handling on tool calls ---
|
|
178
|
-
|
|
197
|
+
// Only flag actual tool execution code, not type definitions or prompts
|
|
198
|
+
if (/callTool\(|executeTool\(|tool\.execute\(/i.test(content) && !/try\s*\{|\.catch\(/i.test(content)) {
|
|
179
199
|
findings.push({
|
|
180
200
|
severity: "warning",
|
|
181
201
|
category: "resilience",
|
|
@@ -186,11 +206,12 @@ export async function scanDirectory(dir: string): Promise<ScanResult> {
|
|
|
186
206
|
}
|
|
187
207
|
|
|
188
208
|
// --- INFO: Agent without timeout ---
|
|
189
|
-
|
|
209
|
+
// Only flag files with actual agent execution logic (not frontend, not configs)
|
|
210
|
+
if (/runAgent|agentWorkflow|executeAgent|startAgent/i.test(content) && /async|await/i.test(content) && !/timeout|AbortSignal|signal/i.test(content)) {
|
|
190
211
|
findings.push({
|
|
191
212
|
severity: "info",
|
|
192
213
|
category: "resilience",
|
|
193
|
-
message: "Agent
|
|
214
|
+
message: "Agent execution without timeout -- could run indefinitely",
|
|
194
215
|
file: relPath,
|
|
195
216
|
fix: "Add timeouts to agent operations to prevent runaway processes",
|
|
196
217
|
});
|
|
@@ -271,6 +292,6 @@ export async function scanDirectory(dir: string): Promise<ScanResult> {
|
|
|
271
292
|
hasDelegation,
|
|
272
293
|
hasAuditLog,
|
|
273
294
|
hasApprovals,
|
|
274
|
-
scannedFiles:
|
|
295
|
+
scannedFiles: backendFiles.length,
|
|
275
296
|
};
|
|
276
297
|
}
|