-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.mjs
executable file
·81 lines (66 loc) · 2.78 KB
/
index.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/usr/bin/env node
/**
* Codebase Scanner by mathiscode
* https://github.com/mathiscode/codebase-scanner
*
* This script scans a folder for files containing malicious code signatures.
**/
import fs from 'node:fs'
import path from 'node:path'
import chalk from 'chalk'
import { program } from 'commander'
import Signatures from './signatures.js'
program
.argument('<folder>', 'The folder to scan')
.option('-f, --fix', 'Fix the files by injecting plain text to prevent the file from running or being imported (default: only scan and report)')
.option('-a, --all', 'Scan all files with all signatures (default: only scan files with matching extensions)')
.option('-l, --limit <size>', 'Set the file size limit in bytes (default: 1000000)', 1000000)
.parse(process.argv)
const folderPath = program.args[0]
const { fix, all, limit } = program.opts()
const maliciousHeader = '======== MALICIOUS ========\nThis file has been flagged as malicious by https://github.com/mathiscode/codebase-scanner\nPlease review the file and remove these lines if appropriate.\n======== MALICIOUS ========\n\n\n\n'
if (!folderPath) {
console.error('Please provide a folder as the first command-line argument.')
process.exit(1)
}
async function iterateFiles(folder) {
if (!fs.existsSync(folder)) return console.error(`Invalid path: ${folder}`)
const files = fs.readdirSync(folder)
for (const file of files) {
const filePath = path.join(folder, file)
const stat = fs.statSync(filePath)
if (stat.isDirectory()) iterateFiles(filePath)
else {
const extension = path.extname(filePath)
const signatures = all ? Signatures : Signatures.filter(s => s.extension === extension?.replace('.', '').toLowerCase())
try {
if (signatures.length > 0) scanFile(filePath, signatures)
} catch (err) {
console.error(chalk.warn(`Error scanning file ${filePath}: ${err.message}`))
}
}
}
}
async function scanFile(file, signatures) {
const stat = await fs.promises.stat(file)
if (stat.size > limit) return // console.log(chalk.yellow('File too large:', file))
let data = await fs.promises.readFile(file, 'utf-8')
// console.log(chalk.blue(`Scanning: ${file} with ${signatures.length} signature(s)...`))
let trigger
for (const { name, signature } of signatures) {
if (trigger) break
const regex = new RegExp(signature, 'g')
if (regex.test(data)) {
trigger = name
if (fix) fs.promises.writeFile(file, `${maliciousHeader}${data}`)
}
}
if (trigger) console.log(chalk.red(`☠️ Found signature ${chalk.white.bgRed(trigger)} in file ${file}`))
if (trigger && fix) console.log(chalk.green(`✅ Fixed file ${file}`))
data = undefined
}
try {
iterateFiles(folderPath)
} catch (error) {
console.error(`An error occurred: ${error.message}`)
}