@safetnsr/vet 1.5.0 → 1.6.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/README.md +2 -0
- package/dist/checks/deps.js +16 -3
- package/dist/checks/verify.js +26 -3
- package/dist/detect-language.d.ts +6 -0
- package/dist/detect-language.js +24 -0
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/checks/deps.js
CHANGED
|
@@ -83,11 +83,14 @@ export function extractPackageName(specifier) {
|
|
|
83
83
|
// Skip node: builtins
|
|
84
84
|
if (specifier.startsWith('node:'))
|
|
85
85
|
return null;
|
|
86
|
+
// Path aliases: @/ is always a path alias (no npm package starts with @/)
|
|
87
|
+
if (specifier.startsWith('@/'))
|
|
88
|
+
return null;
|
|
86
89
|
// Scoped packages: @scope/name or @scope/name/sub
|
|
87
90
|
if (specifier.startsWith('@')) {
|
|
88
91
|
const parts = specifier.split('/');
|
|
89
92
|
if (parts.length < 2)
|
|
90
|
-
return null;
|
|
93
|
+
return null; // bare @scope with no / is not a valid package
|
|
91
94
|
return `${parts[0]}/${parts[1]}`;
|
|
92
95
|
}
|
|
93
96
|
// Regular package: name or name/sub
|
|
@@ -184,15 +187,25 @@ export async function checkDeps(cwd) {
|
|
|
184
187
|
}
|
|
185
188
|
// ── 2. Typosquat detection ─────────────────────────────────────────────────
|
|
186
189
|
const topSet = new Set(TOP_PACKAGES);
|
|
190
|
+
// Known-legitimate short packages that happen to be close to popular ones
|
|
191
|
+
const TYPOSQUAT_WHITELIST = new Set([
|
|
192
|
+
'ai', 'clsx', 'ws', 'os', 'ms', 'pg', 'ip', 'bn', 'qs', 'co', 'is',
|
|
193
|
+
]);
|
|
187
194
|
for (const pkg of declaredNames) {
|
|
188
195
|
if (topSet.has(pkg))
|
|
189
196
|
continue; // it IS the popular package
|
|
197
|
+
if (pkg.length <= 3)
|
|
198
|
+
continue; // too short, too many false matches
|
|
199
|
+
if (TYPOSQUAT_WHITELIST.has(pkg))
|
|
200
|
+
continue;
|
|
190
201
|
for (const top of TOP_PACKAGES) {
|
|
191
202
|
const dist = levenshtein(pkg, top);
|
|
192
203
|
if (dist >= 1 && dist <= 2) {
|
|
204
|
+
// If the package exists on the registry, it's likely legitimate — downgrade to info
|
|
205
|
+
const existsOnRegistry = registryResults.get(pkg) === true;
|
|
193
206
|
issues.push({
|
|
194
|
-
severity: 'error',
|
|
195
|
-
message: `possible typosquat: "${pkg}" is ${dist} edit${dist > 1 ? 's' : ''} from "${top}"`,
|
|
207
|
+
severity: existsOnRegistry ? 'info' : 'error',
|
|
208
|
+
message: `possible typosquat: "${pkg}" is ${dist} edit${dist > 1 ? 's' : ''} from "${top}"${existsOnRegistry ? ' (exists on npm)' : ''}`,
|
|
196
209
|
file: 'package.json',
|
|
197
210
|
fixable: true,
|
|
198
211
|
fixHint: `did you mean "${top}"?`,
|
package/dist/checks/verify.js
CHANGED
|
@@ -83,10 +83,26 @@ function getRecentMessages(cwd, since) {
|
|
|
83
83
|
}
|
|
84
84
|
return raw.split('\n').map(l => l.replace(/^[a-f0-9]+\s+/, '').trim()).filter(l => l.length > 0);
|
|
85
85
|
}
|
|
86
|
+
// ── Python project detection ─────────────────────────────────────────────────
|
|
87
|
+
function isPythonProject(cwd) {
|
|
88
|
+
const markers = ['pyproject.toml', 'setup.py', 'setup.cfg', 'requirements.txt'];
|
|
89
|
+
return markers.some(m => existsSync(join(cwd, m)));
|
|
90
|
+
}
|
|
91
|
+
function isPythonBoilerplate(filePath) {
|
|
92
|
+
const base = basename(filePath);
|
|
93
|
+
if (base === '__init__.py')
|
|
94
|
+
return true;
|
|
95
|
+
if (filePath.endsWith('.pyi'))
|
|
96
|
+
return true;
|
|
97
|
+
if (filePath.replace(/\\/g, '/').includes('__pycache__/'))
|
|
98
|
+
return true;
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
86
101
|
// ── Main check ───────────────────────────────────────────────────────────────
|
|
87
102
|
export function checkVerify(cwd, since) {
|
|
88
103
|
const issues = [];
|
|
89
104
|
let deductions = 0;
|
|
105
|
+
const python = isPythonProject(cwd);
|
|
90
106
|
// Check if git repo
|
|
91
107
|
const isGit = safeExec('git rev-parse --is-inside-work-tree', cwd).trim();
|
|
92
108
|
if (isGit !== 'true') {
|
|
@@ -165,6 +181,11 @@ export function checkVerify(cwd, since) {
|
|
|
165
181
|
}
|
|
166
182
|
const lineCount = countLines(content);
|
|
167
183
|
// 2. File must have meaningful content (>10 non-empty lines)
|
|
184
|
+
// Skip thin file check for Python boilerplate files
|
|
185
|
+
if (python && isPythonBoilerplate(relPath)) {
|
|
186
|
+
verified++;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
168
189
|
if (lineCount < 10 && lineCount > 0) {
|
|
169
190
|
issues.push({
|
|
170
191
|
severity: 'warning',
|
|
@@ -207,13 +228,15 @@ export function checkVerify(cwd, since) {
|
|
|
207
228
|
verified++;
|
|
208
229
|
}
|
|
209
230
|
const finalScore = Math.max(0, 100 - deductions);
|
|
231
|
+
const baseSummary = failed === 0
|
|
232
|
+
? `${verified} agent claim${verified !== 1 ? 's' : ''} verified clean`
|
|
233
|
+
: `${failed} claim${failed !== 1 ? 's' : ''} failed verification (${verified} passed)`;
|
|
234
|
+
const summary = python ? `${baseSummary} (python project detected — some checks have reduced scope)` : baseSummary;
|
|
210
235
|
return {
|
|
211
236
|
name: 'verify',
|
|
212
237
|
score: finalScore,
|
|
213
238
|
maxScore: 100,
|
|
214
239
|
issues,
|
|
215
|
-
summary
|
|
216
|
-
? `${verified} agent claim${verified !== 1 ? 's' : ''} verified clean`
|
|
217
|
-
: `${failed} claim${failed !== 1 ? 's' : ''} failed verification (${verified} passed)`,
|
|
240
|
+
summary,
|
|
218
241
|
};
|
|
219
242
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type ProjectLanguage = 'javascript' | 'typescript' | 'python' | 'unknown';
|
|
2
|
+
/**
|
|
3
|
+
* Detect the primary language of a project by checking for marker files.
|
|
4
|
+
* Priority: typescript > javascript > python > unknown
|
|
5
|
+
*/
|
|
6
|
+
export declare function detectProjectLanguage(cwd: string): ProjectLanguage;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Detect the primary language of a project by checking for marker files.
|
|
5
|
+
* Priority: typescript > javascript > python > unknown
|
|
6
|
+
*/
|
|
7
|
+
export function detectProjectLanguage(cwd) {
|
|
8
|
+
// TypeScript markers
|
|
9
|
+
if (existsSync(join(cwd, 'tsconfig.json')))
|
|
10
|
+
return 'typescript';
|
|
11
|
+
// JavaScript/TypeScript (package.json present)
|
|
12
|
+
if (existsSync(join(cwd, 'package.json'))) {
|
|
13
|
+
// Check if any tsconfig variant exists
|
|
14
|
+
const tsConfigs = ['tsconfig.build.json', 'tsconfig.app.json', 'tsconfig.node.json'];
|
|
15
|
+
if (tsConfigs.some(f => existsSync(join(cwd, f))))
|
|
16
|
+
return 'typescript';
|
|
17
|
+
return 'javascript';
|
|
18
|
+
}
|
|
19
|
+
// Python markers
|
|
20
|
+
const pythonMarkers = ['pyproject.toml', 'setup.py', 'setup.cfg', 'requirements.txt'];
|
|
21
|
+
if (pythonMarkers.some(f => existsSync(join(cwd, f))))
|
|
22
|
+
return 'python';
|
|
23
|
+
return 'unknown';
|
|
24
|
+
}
|