safe-pkg 1.0.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/LICENSE +21 -0
- package/README.md +304 -0
- package/dist/.DS_Store +0 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +135 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/parser.d.ts +5 -0
- package/dist/cli/parser.js +61 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +3 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loadConfig.d.ts +5 -0
- package/dist/config/loadConfig.js +49 -0
- package/dist/config/loadConfig.js.map +1 -0
- package/dist/detector/detectPackageManager.d.ts +14 -0
- package/dist/detector/detectPackageManager.js +68 -0
- package/dist/detector/detectPackageManager.js.map +1 -0
- package/dist/detector/index.d.ts +1 -0
- package/dist/detector/index.js +3 -0
- package/dist/detector/index.js.map +1 -0
- package/dist/executor/index.d.ts +1 -0
- package/dist/executor/index.js +3 -0
- package/dist/executor/index.js.map +1 -0
- package/dist/executor/runCommand.d.ts +9 -0
- package/dist/executor/runCommand.js +61 -0
- package/dist/executor/runCommand.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/scanner/aiAnalyzer.d.ts +6 -0
- package/dist/scanner/aiAnalyzer.js +92 -0
- package/dist/scanner/aiAnalyzer.js.map +1 -0
- package/dist/scanner/analyzePackage.d.ts +5 -0
- package/dist/scanner/analyzePackage.js +155 -0
- package/dist/scanner/analyzePackage.js.map +1 -0
- package/dist/scanner/auditAnalyzer.d.ts +11 -0
- package/dist/scanner/auditAnalyzer.js +113 -0
- package/dist/scanner/auditAnalyzer.js.map +1 -0
- package/dist/scanner/heuristicAnalyzer.d.ts +5 -0
- package/dist/scanner/heuristicAnalyzer.js +228 -0
- package/dist/scanner/heuristicAnalyzer.js.map +1 -0
- package/dist/scanner/index.d.ts +6 -0
- package/dist/scanner/index.js +8 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/metadataAnalyzer.d.ts +5 -0
- package/dist/scanner/metadataAnalyzer.js +136 -0
- package/dist/scanner/metadataAnalyzer.js.map +1 -0
- package/dist/scanner/scriptAnalyzer.d.ts +5 -0
- package/dist/scanner/scriptAnalyzer.js +187 -0
- package/dist/scanner/scriptAnalyzer.js.map +1 -0
- package/dist/scanner-project/batchAnalyze.d.ts +1 -0
- package/dist/scanner-project/batchAnalyze.js +4 -0
- package/dist/scanner-project/batchAnalyze.js.map +1 -0
- package/dist/scanner-project/index.d.ts +4 -0
- package/dist/scanner-project/index.js +6 -0
- package/dist/scanner-project/index.js.map +1 -0
- package/dist/scanner-project/readDependencies.d.ts +5 -0
- package/dist/scanner-project/readDependencies.js +28 -0
- package/dist/scanner-project/readDependencies.js.map +1 -0
- package/dist/scanner-project/scanNodeModules.d.ts +1 -0
- package/dist/scanner-project/scanNodeModules.js +4 -0
- package/dist/scanner-project/scanNodeModules.js.map +1 -0
- package/dist/scanner-project/scanProject.d.ts +5 -0
- package/dist/scanner-project/scanProject.js +69 -0
- package/dist/scanner-project/scanProject.js.map +1 -0
- package/dist/test.d.ts +6 -0
- package/dist/test.js +153 -0
- package/dist/test.js.map +1 -0
- package/dist/types.d.ts +133 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +6 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/logger.d.ts +24 -0
- package/dist/ui/logger.js +39 -0
- package/dist/ui/logger.js.map +1 -0
- package/dist/ui/promptUser.d.ts +12 -0
- package/dist/ui/promptUser.js +50 -0
- package/dist/ui/promptUser.js.map +1 -0
- package/dist/ui/riskReporter.d.ts +5 -0
- package/dist/ui/riskReporter.js +157 -0
- package/dist/ui/riskReporter.js.map +1 -0
- package/dist/ui/scanReporter.d.ts +1 -0
- package/dist/ui/scanReporter.js +4 -0
- package/dist/ui/scanReporter.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Popular legitimate packages (for typosquatting detection)
|
|
3
|
+
*/
|
|
4
|
+
const POPULAR_PACKAGES = [
|
|
5
|
+
"react",
|
|
6
|
+
"vue",
|
|
7
|
+
"angular",
|
|
8
|
+
"express",
|
|
9
|
+
"lodash",
|
|
10
|
+
"axios",
|
|
11
|
+
"moment",
|
|
12
|
+
"webpack",
|
|
13
|
+
"babel",
|
|
14
|
+
"eslint",
|
|
15
|
+
"prettier",
|
|
16
|
+
"typescript",
|
|
17
|
+
"jquery",
|
|
18
|
+
"chalk",
|
|
19
|
+
"commander",
|
|
20
|
+
"next",
|
|
21
|
+
"gatsby",
|
|
22
|
+
"nuxt",
|
|
23
|
+
"svelte",
|
|
24
|
+
"redux",
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* Suspicious keywords in package names
|
|
28
|
+
*/
|
|
29
|
+
const SUSPICIOUS_KEYWORDS = [
|
|
30
|
+
"hack",
|
|
31
|
+
"crack",
|
|
32
|
+
"keygen",
|
|
33
|
+
"exploit",
|
|
34
|
+
"backdoor",
|
|
35
|
+
"malware",
|
|
36
|
+
"virus",
|
|
37
|
+
"trojan",
|
|
38
|
+
"stealer",
|
|
39
|
+
"miner",
|
|
40
|
+
"cryptominer",
|
|
41
|
+
"bitcoin",
|
|
42
|
+
"wallet-stealer",
|
|
43
|
+
"free-premium",
|
|
44
|
+
"pro-unlimited",
|
|
45
|
+
"cracked",
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* Suspicious patterns in package names
|
|
49
|
+
*/
|
|
50
|
+
const SUSPICIOUS_PATTERNS = [
|
|
51
|
+
/^[a-z0-9-]{50,}$/, // Extremely long random names
|
|
52
|
+
/^[0-9]+$/, // All numbers
|
|
53
|
+
/([a-z])\1{4,}/, // Repeated characters (aaaaa)
|
|
54
|
+
/^(test|tmp|temp|demo|sample)-/, // Temporary/test packages
|
|
55
|
+
/-+admin-*$/i, // Ends with admin
|
|
56
|
+
/-+pro-*max/i, // Contains "pro max" marketing spam
|
|
57
|
+
/free.*premium/i, // "free premium" scam
|
|
58
|
+
/unlimited.*crack/i, // "unlimited crack"
|
|
59
|
+
];
|
|
60
|
+
/**
|
|
61
|
+
* Analyze package name using heuristic patterns
|
|
62
|
+
*/
|
|
63
|
+
export function analyzeHeuristics(packageName) {
|
|
64
|
+
const suspiciousPatterns = [];
|
|
65
|
+
let riskScore = 0;
|
|
66
|
+
// Check for suspicious keywords
|
|
67
|
+
const foundKeywords = checkSuspiciousKeywords(packageName);
|
|
68
|
+
if (foundKeywords.length > 0) {
|
|
69
|
+
suspiciousPatterns.push(`Suspicious keywords: ${foundKeywords.join(", ")}`);
|
|
70
|
+
riskScore += foundKeywords.length * 3; // 3 points per suspicious keyword
|
|
71
|
+
}
|
|
72
|
+
// Check for suspicious patterns
|
|
73
|
+
const foundPatterns = checkSuspiciousPatterns(packageName);
|
|
74
|
+
if (foundPatterns.length > 0) {
|
|
75
|
+
suspiciousPatterns.push(...foundPatterns);
|
|
76
|
+
riskScore += foundPatterns.length * 2; // 2 points per pattern
|
|
77
|
+
}
|
|
78
|
+
// Check for typosquatting
|
|
79
|
+
const typosquatResult = checkTyposquatting(packageName);
|
|
80
|
+
if (typosquatResult.isPotentialTyposquat) {
|
|
81
|
+
suspiciousPatterns.push(`Possible typosquatting of: ${typosquatResult.similarTo}`);
|
|
82
|
+
riskScore += 4; // High risk for typosquatting
|
|
83
|
+
}
|
|
84
|
+
// Check for excessive hyphens/numbers
|
|
85
|
+
const hyphenCount = (packageName.match(/-/g) || []).length;
|
|
86
|
+
const numberCount = (packageName.match(/[0-9]/g) || []).length;
|
|
87
|
+
if (hyphenCount > 4) {
|
|
88
|
+
suspiciousPatterns.push(`Excessive hyphens (${hyphenCount})`);
|
|
89
|
+
riskScore += 1;
|
|
90
|
+
}
|
|
91
|
+
if (numberCount > packageName.length / 2) {
|
|
92
|
+
suspiciousPatterns.push("Too many numbers in name");
|
|
93
|
+
riskScore += 1;
|
|
94
|
+
}
|
|
95
|
+
// Check for mixed case spam (e.g., "rEaCt-FrEe")
|
|
96
|
+
if (/[A-Z]/.test(packageName) && /[a-z]/.test(packageName)) {
|
|
97
|
+
const upperCount = (packageName.match(/[A-Z]/g) || []).length;
|
|
98
|
+
const lowerCount = (packageName.match(/[a-z]/g) || []).length;
|
|
99
|
+
if (upperCount > 2 && upperCount < lowerCount) {
|
|
100
|
+
suspiciousPatterns.push("Mixed case pattern (possible spam)");
|
|
101
|
+
riskScore += 1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Cap score at 10
|
|
105
|
+
riskScore = Math.min(riskScore, 10);
|
|
106
|
+
return {
|
|
107
|
+
suspiciousPatterns,
|
|
108
|
+
isPotentialTyposquat: typosquatResult.isPotentialTyposquat,
|
|
109
|
+
riskScore: riskScore,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check for suspicious keywords in package name
|
|
114
|
+
*/
|
|
115
|
+
function checkSuspiciousKeywords(packageName) {
|
|
116
|
+
const lowerName = packageName.toLowerCase();
|
|
117
|
+
return SUSPICIOUS_KEYWORDS.filter((keyword) => lowerName.includes(keyword));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check for suspicious patterns in package name
|
|
121
|
+
*/
|
|
122
|
+
function checkSuspiciousPatterns(packageName) {
|
|
123
|
+
const patterns = [];
|
|
124
|
+
for (const pattern of SUSPICIOUS_PATTERNS) {
|
|
125
|
+
if (pattern.test(packageName)) {
|
|
126
|
+
patterns.push(`Matches suspicious pattern: ${pattern.source}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return patterns;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if package name is potentially typosquatting a popular package
|
|
133
|
+
*/
|
|
134
|
+
function checkTyposquatting(packageName) {
|
|
135
|
+
const lowerName = packageName.toLowerCase();
|
|
136
|
+
for (const popularPkg of POPULAR_PACKAGES) {
|
|
137
|
+
// Exact match is not typosquatting
|
|
138
|
+
if (lowerName === popularPkg.toLowerCase()) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
// Check Levenshtein distance
|
|
142
|
+
const distance = levenshteinDistance(lowerName, popularPkg.toLowerCase());
|
|
143
|
+
// If distance is 1-2, it's likely typosquatting
|
|
144
|
+
if (distance <= 2 && distance > 0) {
|
|
145
|
+
return {
|
|
146
|
+
isPotentialTyposquat: true,
|
|
147
|
+
similarTo: popularPkg,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// Check for common typosquatting techniques
|
|
151
|
+
if (checkCommonTyposquattingTechniques(lowerName, popularPkg)) {
|
|
152
|
+
return {
|
|
153
|
+
isPotentialTyposquat: true,
|
|
154
|
+
similarTo: popularPkg,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
isPotentialTyposquat: false,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Calculate Levenshtein distance between two strings
|
|
164
|
+
*/
|
|
165
|
+
function levenshteinDistance(str1, str2) {
|
|
166
|
+
const len1 = str1.length;
|
|
167
|
+
const len2 = str2.length;
|
|
168
|
+
// Create matrix with proper initialization
|
|
169
|
+
const matrix = [];
|
|
170
|
+
for (let i = 0; i <= len1; i++) {
|
|
171
|
+
matrix[i] = new Array(len2 + 1).fill(0);
|
|
172
|
+
}
|
|
173
|
+
// Initialize first column and row
|
|
174
|
+
for (let i = 0; i <= len1; i++) {
|
|
175
|
+
const row = matrix[i];
|
|
176
|
+
if (row)
|
|
177
|
+
row[0] = i;
|
|
178
|
+
}
|
|
179
|
+
for (let j = 0; j <= len2; j++) {
|
|
180
|
+
const row = matrix[0];
|
|
181
|
+
if (row)
|
|
182
|
+
row[j] = j;
|
|
183
|
+
}
|
|
184
|
+
// Fill matrix
|
|
185
|
+
for (let i = 1; i <= len1; i++) {
|
|
186
|
+
for (let j = 1; j <= len2; j++) {
|
|
187
|
+
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
|
188
|
+
const currentRow = matrix[i];
|
|
189
|
+
const prevRow = matrix[i - 1];
|
|
190
|
+
if (currentRow && prevRow) {
|
|
191
|
+
const deletion = (prevRow[j] ?? 0) + 1;
|
|
192
|
+
const insertion = (currentRow[j - 1] ?? 0) + 1;
|
|
193
|
+
const substitution = (prevRow[j - 1] ?? 0) + cost;
|
|
194
|
+
currentRow[j] = Math.min(deletion, insertion, substitution);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const finalRow = matrix[len1];
|
|
199
|
+
return finalRow ? finalRow[len2] ?? 0 : 0;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Check for common typosquatting techniques
|
|
203
|
+
*/
|
|
204
|
+
function checkCommonTyposquattingTechniques(packageName, popularPkg) {
|
|
205
|
+
// Adding prefix/suffix
|
|
206
|
+
if (packageName.startsWith(popularPkg) || packageName.endsWith(popularPkg)) {
|
|
207
|
+
const diff = packageName.replace(popularPkg, "");
|
|
208
|
+
// If the difference is small and looks like spam
|
|
209
|
+
if (diff.length <= 5 && /^[-_0-9]+$/.test(diff)) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Character substitution (l -> 1, o -> 0, etc.)
|
|
214
|
+
const normalized = packageName
|
|
215
|
+
.replace(/1/g, "l")
|
|
216
|
+
.replace(/0/g, "o")
|
|
217
|
+
.replace(/5/g, "s");
|
|
218
|
+
if (normalized === popularPkg) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
// Extra hyphen/underscore
|
|
222
|
+
const withoutSeparators = packageName.replace(/[-_]/g, "");
|
|
223
|
+
if (withoutSeparators === popularPkg.replace(/[-_]/g, "")) {
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=heuristicAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heuristicAnalyzer.js","sourceRoot":"","sources":["../../src/scanner/heuristicAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./analyzePackage.js";
|
|
2
|
+
export { analyzeWithAudit, analyzePackageAudit } from "./auditAnalyzer.js";
|
|
3
|
+
export * from "./metadataAnalyzer.js";
|
|
4
|
+
export * from "./scriptAnalyzer.js";
|
|
5
|
+
export * from "./heuristicAnalyzer.js";
|
|
6
|
+
export * from "./aiAnalyzer.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Scanner barrel exports
|
|
2
|
+
export * from "./analyzePackage.js";
|
|
3
|
+
export { analyzeWithAudit, analyzePackageAudit } from "./auditAnalyzer.js";
|
|
4
|
+
export * from "./metadataAnalyzer.js";
|
|
5
|
+
export * from "./scriptAnalyzer.js";
|
|
6
|
+
export * from "./heuristicAnalyzer.js";
|
|
7
|
+
export * from "./aiAnalyzer.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyze package metadata from npm registry
|
|
3
|
+
*/
|
|
4
|
+
export async function analyzeMetadata(packageName) {
|
|
5
|
+
try {
|
|
6
|
+
// Fetch package data from npm registry
|
|
7
|
+
const packageData = await fetchPackageData(packageName);
|
|
8
|
+
const downloads = await fetchDownloadsData(packageName);
|
|
9
|
+
// Calculate metadata score
|
|
10
|
+
return calculateMetadataScore(packageData, downloads);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
// If fetching fails, return a default risky score
|
|
14
|
+
return createDefaultMetadataScore();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Fetch package information from npm registry
|
|
19
|
+
*/
|
|
20
|
+
async function fetchPackageData(packageName) {
|
|
21
|
+
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`);
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error(`Package not found: ${packageName}`);
|
|
24
|
+
}
|
|
25
|
+
return response.json();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Fetch download statistics from npm API
|
|
29
|
+
*/
|
|
30
|
+
async function fetchDownloadsData(packageName) {
|
|
31
|
+
const response = await fetch(`https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(packageName)}`);
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
// No download data available, return zero
|
|
34
|
+
return {
|
|
35
|
+
downloads: 0,
|
|
36
|
+
start: "",
|
|
37
|
+
end: "",
|
|
38
|
+
package: packageName,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return response.json();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Calculate metadata risk score based on package data
|
|
45
|
+
*/
|
|
46
|
+
function calculateMetadataScore(packageData, downloadsData) {
|
|
47
|
+
const now = new Date();
|
|
48
|
+
const weeklyDownloads = downloadsData.downloads;
|
|
49
|
+
// Get latest version publish date
|
|
50
|
+
const latestVersion = packageData["dist-tags"]?.latest || "";
|
|
51
|
+
const publishDateStr = packageData.time?.[latestVersion];
|
|
52
|
+
const lastPublishDate = publishDateStr ? new Date(publishDateStr) : now;
|
|
53
|
+
// Get package creation date (first version)
|
|
54
|
+
const createdDateStr = packageData.time?.created;
|
|
55
|
+
const createdDate = createdDateStr ? new Date(createdDateStr) : now;
|
|
56
|
+
// Calculate package age in days
|
|
57
|
+
const packageAge = Math.floor((now.getTime() - createdDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
58
|
+
// Calculate days since last publish
|
|
59
|
+
const daysSincePublish = Math.floor((now.getTime() - lastPublishDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
60
|
+
// Get maintainer info
|
|
61
|
+
const maintainerCount = packageData.maintainers?.length || 0;
|
|
62
|
+
const hasLicense = Boolean(packageData.license);
|
|
63
|
+
// Calculate risk score (0-10, higher = more risky)
|
|
64
|
+
let riskScore = 0;
|
|
65
|
+
// Downloads score (0-3 points)
|
|
66
|
+
if (weeklyDownloads < 100) {
|
|
67
|
+
riskScore += 3; // Very low downloads - very risky
|
|
68
|
+
}
|
|
69
|
+
else if (weeklyDownloads < 1000) {
|
|
70
|
+
riskScore += 2; // Low downloads - risky
|
|
71
|
+
}
|
|
72
|
+
else if (weeklyDownloads < 10000) {
|
|
73
|
+
riskScore += 1; // Moderate downloads - slight risk
|
|
74
|
+
}
|
|
75
|
+
// Over 10k downloads = 0 points (safe)
|
|
76
|
+
// Last publish date score (0-3 points)
|
|
77
|
+
if (daysSincePublish > 730) {
|
|
78
|
+
// > 2 years
|
|
79
|
+
riskScore += 3; // Abandoned
|
|
80
|
+
}
|
|
81
|
+
else if (daysSincePublish > 365) {
|
|
82
|
+
// > 1 year
|
|
83
|
+
riskScore += 2; // Stale
|
|
84
|
+
}
|
|
85
|
+
else if (daysSincePublish > 180) {
|
|
86
|
+
// > 6 months
|
|
87
|
+
riskScore += 1; // Moderately maintained
|
|
88
|
+
}
|
|
89
|
+
// Updated within 6 months = 0 points (well maintained)
|
|
90
|
+
// Package age score (0-2 points)
|
|
91
|
+
if (packageAge < 30) {
|
|
92
|
+
// Less than 1 month old
|
|
93
|
+
riskScore += 2; // Too new, unproven
|
|
94
|
+
}
|
|
95
|
+
else if (packageAge < 90) {
|
|
96
|
+
// Less than 3 months old
|
|
97
|
+
riskScore += 1; // Relatively new
|
|
98
|
+
}
|
|
99
|
+
// Older than 3 months = 0 points (established)
|
|
100
|
+
// Maintainer count score (0-1 point)
|
|
101
|
+
if (maintainerCount === 0) {
|
|
102
|
+
riskScore += 1; // No maintainers listed
|
|
103
|
+
}
|
|
104
|
+
// Has maintainers = 0 points
|
|
105
|
+
// License score (0-1 point)
|
|
106
|
+
if (!hasLicense) {
|
|
107
|
+
riskScore += 1; // No license
|
|
108
|
+
}
|
|
109
|
+
// Has license = 0 points
|
|
110
|
+
// Cap score at 10
|
|
111
|
+
riskScore = Math.min(riskScore, 10);
|
|
112
|
+
return {
|
|
113
|
+
version: packageData["dist-tags"]?.latest ?? "unknown",
|
|
114
|
+
downloads: weeklyDownloads,
|
|
115
|
+
lastPublishDate,
|
|
116
|
+
maintainerCount,
|
|
117
|
+
hasLicense,
|
|
118
|
+
packageAge,
|
|
119
|
+
riskScore: riskScore,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Create default metadata score for when fetching fails
|
|
124
|
+
*/
|
|
125
|
+
function createDefaultMetadataScore() {
|
|
126
|
+
return {
|
|
127
|
+
version: "unknown",
|
|
128
|
+
downloads: 0,
|
|
129
|
+
lastPublishDate: new Date(),
|
|
130
|
+
maintainerCount: 0,
|
|
131
|
+
hasLicense: false,
|
|
132
|
+
packageAge: 0,
|
|
133
|
+
riskScore: 8, // High risk when we can't verify metadata
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=metadataAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadataAnalyzer.js","sourceRoot":"","sources":["../../src/scanner/metadataAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dangerous commands that are red flags in package scripts
|
|
3
|
+
*/
|
|
4
|
+
const DANGEROUS_COMMANDS = [
|
|
5
|
+
"curl",
|
|
6
|
+
"wget",
|
|
7
|
+
"eval",
|
|
8
|
+
"chmod",
|
|
9
|
+
"chown",
|
|
10
|
+
"sudo",
|
|
11
|
+
"rm -rf",
|
|
12
|
+
"dd if=",
|
|
13
|
+
"mkfs",
|
|
14
|
+
":(){ :|:& };:", // Fork bomb
|
|
15
|
+
"/dev/sda",
|
|
16
|
+
"/dev/null",
|
|
17
|
+
">/dev/",
|
|
18
|
+
"format c:",
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Suspicious commands that warrant investigation
|
|
22
|
+
*/
|
|
23
|
+
const SUSPICIOUS_COMMANDS = [
|
|
24
|
+
"nc ", // netcat
|
|
25
|
+
"telnet",
|
|
26
|
+
"ssh",
|
|
27
|
+
"scp",
|
|
28
|
+
"ftp",
|
|
29
|
+
"base64",
|
|
30
|
+
"exec",
|
|
31
|
+
"child_process",
|
|
32
|
+
"execSync",
|
|
33
|
+
"spawn",
|
|
34
|
+
".decrypt",
|
|
35
|
+
"crypto",
|
|
36
|
+
];
|
|
37
|
+
/**
|
|
38
|
+
* Network-related commands
|
|
39
|
+
*/
|
|
40
|
+
const NETWORK_COMMANDS = [
|
|
41
|
+
"http://",
|
|
42
|
+
"https://",
|
|
43
|
+
"fetch(",
|
|
44
|
+
"axios",
|
|
45
|
+
"request(",
|
|
46
|
+
"download",
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* Lifecycle hooks that run automatically
|
|
50
|
+
*/
|
|
51
|
+
const LIFECYCLE_HOOKS = [
|
|
52
|
+
"preinstall",
|
|
53
|
+
"install",
|
|
54
|
+
"postinstall",
|
|
55
|
+
"preuninstall",
|
|
56
|
+
"uninstall",
|
|
57
|
+
"postuninstall",
|
|
58
|
+
"preversion",
|
|
59
|
+
"version",
|
|
60
|
+
"postversion",
|
|
61
|
+
];
|
|
62
|
+
/**
|
|
63
|
+
* Analyze package.json scripts for security risks
|
|
64
|
+
*/
|
|
65
|
+
export async function analyzeScripts(packageName) {
|
|
66
|
+
try {
|
|
67
|
+
// Fetch package.json from npm registry
|
|
68
|
+
const packageJson = await fetchPackageJson(packageName);
|
|
69
|
+
if (!packageJson.scripts) {
|
|
70
|
+
// No scripts = safe
|
|
71
|
+
return createSafeScriptResult();
|
|
72
|
+
}
|
|
73
|
+
// Analyze the scripts
|
|
74
|
+
return analyzePackageScripts(packageJson.scripts);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// If fetching fails, assume safe (no scripts to analyze)
|
|
78
|
+
return createSafeScriptResult();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Fetch package.json from npm registry
|
|
83
|
+
*/
|
|
84
|
+
async function fetchPackageJson(packageName) {
|
|
85
|
+
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`);
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
throw new Error(`Package not found: ${packageName}`);
|
|
88
|
+
}
|
|
89
|
+
return response.json();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Analyze scripts object for suspicious patterns
|
|
93
|
+
*/
|
|
94
|
+
function analyzePackageScripts(scripts) {
|
|
95
|
+
const suspiciousScripts = [];
|
|
96
|
+
const dangerousCommands = [];
|
|
97
|
+
let riskScore = 0;
|
|
98
|
+
// Check each script
|
|
99
|
+
for (const [scriptName, scriptContent] of Object.entries(scripts)) {
|
|
100
|
+
const analysis = analyzeScript(scriptName, scriptContent);
|
|
101
|
+
if (analysis.isDangerous) {
|
|
102
|
+
suspiciousScripts.push(`${scriptName}: ${analysis.reason || "dangerous command"}`);
|
|
103
|
+
dangerousCommands.push(...analysis.commands);
|
|
104
|
+
riskScore += analysis.riskPoints;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Check for automatic lifecycle hooks
|
|
108
|
+
const hasLifecycleHooks = LIFECYCLE_HOOKS.some((hook) => Object.keys(scripts).includes(hook));
|
|
109
|
+
if (hasLifecycleHooks) {
|
|
110
|
+
const lifecycleScripts = LIFECYCLE_HOOKS.filter((hook) => Object.keys(scripts).includes(hook));
|
|
111
|
+
suspiciousScripts.push(`Automatic lifecycle hooks: ${lifecycleScripts.join(", ")}`);
|
|
112
|
+
riskScore += 2; // Additional risk for automatic execution
|
|
113
|
+
}
|
|
114
|
+
// Cap the score at 10
|
|
115
|
+
riskScore = Math.min(riskScore, 10);
|
|
116
|
+
const hasSuspiciousScripts = suspiciousScripts.length > 0;
|
|
117
|
+
return {
|
|
118
|
+
hasSuspiciousScripts,
|
|
119
|
+
suspiciousScripts,
|
|
120
|
+
dangerousCommands: [...new Set(dangerousCommands)], // Remove duplicates
|
|
121
|
+
riskScore: riskScore,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Analyze individual script for suspicious patterns
|
|
126
|
+
*/
|
|
127
|
+
function analyzeScript(scriptName, scriptContent) {
|
|
128
|
+
const lowerContent = scriptContent.toLowerCase();
|
|
129
|
+
const foundCommands = [];
|
|
130
|
+
let riskPoints = 0;
|
|
131
|
+
let reason = "";
|
|
132
|
+
// Check for dangerous commands
|
|
133
|
+
for (const cmd of DANGEROUS_COMMANDS) {
|
|
134
|
+
if (lowerContent.includes(cmd.toLowerCase())) {
|
|
135
|
+
foundCommands.push(cmd);
|
|
136
|
+
riskPoints += 5; // Very high risk
|
|
137
|
+
reason = `dangerous command: ${cmd}`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Check for suspicious commands
|
|
141
|
+
for (const cmd of SUSPICIOUS_COMMANDS) {
|
|
142
|
+
if (lowerContent.includes(cmd.toLowerCase())) {
|
|
143
|
+
foundCommands.push(cmd);
|
|
144
|
+
riskPoints += 2; // Medium risk
|
|
145
|
+
if (!reason)
|
|
146
|
+
reason = `suspicious command: ${cmd}`;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Check for network commands
|
|
150
|
+
for (const cmd of NETWORK_COMMANDS) {
|
|
151
|
+
if (lowerContent.includes(cmd.toLowerCase())) {
|
|
152
|
+
foundCommands.push(cmd);
|
|
153
|
+
riskPoints += 1; // Low risk (network calls are common)
|
|
154
|
+
if (!reason)
|
|
155
|
+
reason = `network activity: ${cmd}`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Check for obfuscated code patterns
|
|
159
|
+
if (lowerContent.includes("\\x") ||
|
|
160
|
+
lowerContent.includes("\\u") ||
|
|
161
|
+
lowerContent.match(/[a-z0-9]{50,}/) // Long random strings
|
|
162
|
+
) {
|
|
163
|
+
foundCommands.push("obfuscated code");
|
|
164
|
+
riskPoints += 3;
|
|
165
|
+
if (!reason)
|
|
166
|
+
reason = "potentially obfuscated code";
|
|
167
|
+
}
|
|
168
|
+
const isDangerous = foundCommands.length > 0;
|
|
169
|
+
return {
|
|
170
|
+
isDangerous,
|
|
171
|
+
reason,
|
|
172
|
+
commands: foundCommands,
|
|
173
|
+
riskPoints,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Create a safe script result (no suspicious scripts found)
|
|
178
|
+
*/
|
|
179
|
+
function createSafeScriptResult() {
|
|
180
|
+
return {
|
|
181
|
+
hasSuspiciousScripts: false,
|
|
182
|
+
suspiciousScripts: [],
|
|
183
|
+
dangerousCommands: [],
|
|
184
|
+
riskScore: 0,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=scriptAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scriptAnalyzer.js","sourceRoot":"","sources":["../../src/scanner/scriptAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batchAnalyze.js","sourceRoot":"","sources":["../../src/scanner-project/batchAnalyze.ts"],"names":[],"mappings":";AAAA,yBAAyB;AACzB,iCAAiC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanner-project/index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC"}
|