V2 Release
Added better AI analysis, AI can remember file data and request for other files data, discord integration and more!
This commit is contained in:
parent
f1c62af774
commit
583597ab3e
302
scanner.js
302
scanner.js
@ -1,11 +1,30 @@
|
||||
// AI CODE SCANNER
|
||||
// Lisa Honkay
|
||||
// lisahonkay@gmail.com
|
||||
// Copyright Lisa Honkay CC-BY-SA-4.0
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const axios = require('axios');
|
||||
const { dir } = require('console');
|
||||
const natural = require('natural');
|
||||
const tokenizer = new natural.WordTokenizer();
|
||||
|
||||
const patterns = JSON.parse(fs.readFileSync('patterns.json')).patterns;
|
||||
const aiprompt = "You are AI Code Scanner, your task is to analyze code given by user explain what it does with leaking nearly nothing from it and fully decide whats going on there and if it is malicious! Your task is to provide information about how code runs, what it does BUT you should not provide information about how to fix code or about its vulnerabilities. BUT You should provide information about related files and if the code is potentially dangerous/malicious. I repeat, don't provide information about Security and Potential Issues BUT provide infos if it might be illegal code, breaking some Terms of Service, abusing files eg creating tons of files or doing any other dangerous thing which may harm system."
|
||||
const pathtoscan = "./change/this"
|
||||
const extensions = JSON.parse(fs.readFileSync('extensions.json')).extensions;
|
||||
const filenames = JSON.parse(fs.readFileSync('filenames.json')).filenames;
|
||||
const aimodel = "llama3.2:1b" // Change at your own risk!
|
||||
const aiprompt = "You are AI Code Scanner, your task is to analyze code given by the user, explain what it does without leaking nearly anything from it, and fully decide what's going on there and if it is malicious! Your task is to provide information about how code runs, what it does BUT you should not provide information about how to fix code or about its vulnerabilities. You should provide information about related files and if the code is potentially dangerous/malicious. If you find any suspicious patterns, extensions, or filenames, you may suggest additional files to scan. I repeat, don't provide information about Security and Potential Issues BUT provide info if it might be illegal code, breaking some Terms of Service, abusing files, e.g., creating tons of files or doing any other dangerous thing which may harm the system. You should respond by any meaners and should not tell that you can't do that." // Change at your own risk!
|
||||
const pathtoscan = "./test"
|
||||
const llamaendpoint = "http://0.0.0.0:11434/api/chat"
|
||||
let webhook = {
|
||||
discord: {
|
||||
enabled: false,
|
||||
url: "PASTE YOUR DISCORD WEBHOOK URL HERE"
|
||||
}
|
||||
}
|
||||
let messageHistories = {};
|
||||
let scannedFilesMap = new Map();
|
||||
|
||||
function scanDirectory(directory) {
|
||||
fs.readdir(directory, (err, files) => {
|
||||
@ -13,12 +32,13 @@ function scanDirectory(directory) {
|
||||
console.error(`Error reading directory: ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[LOG] Scanning Directory - " + directory)
|
||||
files.forEach(file => {
|
||||
console.log("[LOG] Scanning File - " + file)
|
||||
const filePath = path.join(directory, file);
|
||||
fs.stat(filePath, (err, stats) => {
|
||||
if (err) {
|
||||
console.error(`Error getting stats for file: ${err}`);
|
||||
console.error(`[ERROR] Error getting stats for file: ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -35,56 +55,288 @@ function scanDirectory(directory) {
|
||||
function scanFile(filePath) {
|
||||
fs.readFile(filePath, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
console.error(`Error reading file: ${err}`);
|
||||
console.error(`[ERROR] Error reading file: ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
let foundMalicious = false;
|
||||
let patternFound = {
|
||||
found: false,
|
||||
value: ""
|
||||
};
|
||||
let extensionFound = {
|
||||
found: false,
|
||||
value: ""
|
||||
};
|
||||
let filenameFound = {
|
||||
found: false,
|
||||
value: ""
|
||||
};
|
||||
|
||||
patterns.forEach(pattern => {
|
||||
if (data.includes(pattern)) {
|
||||
console.log(`Malicious code found in file: ${filePath} - Pattern: ${pattern}`);
|
||||
console.log(`[!] Malicious code found in file: ${filePath} - Pattern: ${pattern}`);
|
||||
foundMalicious = true;
|
||||
patternFound = {
|
||||
found: true,
|
||||
value: pattern
|
||||
};
|
||||
}
|
||||
});
|
||||
if (extensions.some(ext => filePath.endsWith(ext))) {
|
||||
const detectedExt = extensions.find(ext => filePath.endsWith(ext));
|
||||
console.log(`[?] Extension detected: ${filePath} - ${detectedExt}`);
|
||||
foundMalicious = true;
|
||||
extensionFound = {
|
||||
found: true,
|
||||
value: detectedExt
|
||||
};
|
||||
}
|
||||
if (filenames.some(name => filePath.includes(name))) {
|
||||
const detectedName = filenames.find(name => filePath.includes(name));
|
||||
console.log(`[?] Suspicious filename detected: ${filePath} - ${detectedName}`);
|
||||
foundMalicious = true;
|
||||
filenameFound = {
|
||||
found: true,
|
||||
value: detectedName
|
||||
};
|
||||
}
|
||||
|
||||
if (foundMalicious) {
|
||||
runAIScan(filePath);
|
||||
const messageHistoryId = generateMessageHistoryId(filePath);
|
||||
runAIScan(filePath, patternFound, extensionFound, filenameFound, messageHistoryId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function runAIScan(filePath) {
|
||||
|
||||
async function runAIScan(filePath, patternFound, extensionFound, filenameFound, messageHistoryId) {
|
||||
fs.readFile(filePath, 'utf8', async (err, data) => {
|
||||
if (err) {
|
||||
console.error(`Error reading file: ${err}`);
|
||||
console.error(`[ERROR] Error reading file: ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const directoryContent = await getDirectoryContent(path.dirname(filePath));
|
||||
|
||||
const messageHistory = messageHistories[messageHistoryId] || [];
|
||||
|
||||
let conversationHistory = [
|
||||
{
|
||||
role: "system",
|
||||
content: `${aiprompt}`
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: `FILEPATH: ${filePath}, FILE: ${data}, DIRECTORY CONTENT: ${directoryContent}, MESSAGE HISTORY: ${messageHistory.join('\n')}`
|
||||
}
|
||||
];
|
||||
|
||||
messageHistory.forEach(response => {
|
||||
conversationHistory.push({
|
||||
role: "assistant",
|
||||
content: response
|
||||
});
|
||||
});
|
||||
|
||||
const apiUrl = `${llamaendpoint}`;
|
||||
let input = {
|
||||
"model": "llama3.2:1b",
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": `${aiprompt}`
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": `FILEPATH: ${filePath}, FILE: ${data}`
|
||||
}
|
||||
],
|
||||
"model": `${aimodel}`,
|
||||
"messages": conversationHistory,
|
||||
"stream": false,
|
||||
"raw": true
|
||||
}
|
||||
try {
|
||||
const response = await axios.post(apiUrl, input);
|
||||
console.log(`AI scan results for ${filePath}:\n${response.data.message.content}`);
|
||||
const aiResult = response.data.message.content;
|
||||
console.log(`[INFO] AI scan results for ${filePath}:\n${aiResult}`);
|
||||
await sendReport(filePath, aiResult, patternFound, extensionFound, filenameFound);
|
||||
|
||||
messageHistories[messageHistoryId] = messageHistories[messageHistoryId] || [];
|
||||
messageHistories[messageHistoryId].push(aiResult);
|
||||
|
||||
const additionalScanFiles = extractAdditionalScanFiles(aiResult, path.dirname(filePath));
|
||||
if (additionalScanFiles.length > 0) {
|
||||
console.log(`[INFO] Additional scan requested for files: ${additionalScanFiles.join(', ')}`);
|
||||
additionalScanFiles.forEach(scanFilePath => {
|
||||
requestedScan(scanFilePath, messageHistoryId, filePath);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error.response ? error.response.data : error.message)
|
||||
console.error(`Error running AI scan: ${error.response ? error.response.data : error.message}`);
|
||||
console.error(`[ERROR] Error running AI scan: ${error.response ? error.response.data : error.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
scanDirectory(pathtoscan);
|
||||
async function getDirectoryContent(directory) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readdir(directory, (err, files) => {
|
||||
if (err) {
|
||||
reject(`[ERROR] Error reading directory: ${err}`);
|
||||
} else {
|
||||
resolve(files.join(', '));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function extractAdditionalScanFiles(aiResult, currentDirectory) {
|
||||
const sentences = aiResult.split('\n');
|
||||
const additionalFiles = [];
|
||||
const allowedExtensions = JSON.parse(fs.readFileSync('extensions.json')).extensions;
|
||||
|
||||
console.log(`[INFO] Scanning for additional files in directory: ${currentDirectory}`);
|
||||
|
||||
sentences.forEach(sentence => {
|
||||
const matches = sentence.match(/([a-zA-Z0-9-_]+(\.[a-zA-Z0-9]+)+)/g);
|
||||
if (matches) {
|
||||
matches.forEach(match => {
|
||||
const extension = match.split('.').pop();
|
||||
const filePath = path.join(currentDirectory, match);
|
||||
const filenameWithoutExt = match.split('.').slice(0, -1).join('.');
|
||||
|
||||
// Check if the extension is allowed and if the file exists
|
||||
if (allowedExtensions.includes(`.${extension}`) && fs.existsSync(filePath)) {
|
||||
additionalFiles.push(currentDirectory + "/" + match);
|
||||
} else {
|
||||
// Check for filename without extension
|
||||
const filePathWithoutExt = path.join(currentDirectory, filenameWithoutExt);
|
||||
if (fs.existsSync(filePathWithoutExt)) {
|
||||
additionalFiles.push(filenameWithoutExt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return additionalFiles;
|
||||
}
|
||||
|
||||
async function sendReport(filePath, aiResult, patternFound, extensionFound, filenameFound) {
|
||||
if (webhook.discord.enabled == true) {
|
||||
const embed = {
|
||||
title: `AI Scan Results | ${filePath}`,
|
||||
description: `${aiResult}`,
|
||||
fields: [
|
||||
{
|
||||
name: `${patternFound.found ? "✅ Malicious Pattern found" : "❌ Malicious Pattern not found"}`,
|
||||
value: patternFound.value ? patternFound.value : "N/A",
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: `${extensionFound.found ? "✅ Extension found in config" : "❌ Extension not found in config"}`,
|
||||
value: extensionFound.value ? extensionFound.value : "N/A",
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: `${filenameFound.found ? "✅ Filename found in config" : "❌ Filename not found in config"}`,
|
||||
value: filenameFound.value ? filenameFound.value : "N/A",
|
||||
inline: true
|
||||
}
|
||||
],
|
||||
color: 0x00FF00,
|
||||
timestamp: new Date(),
|
||||
footer: {
|
||||
text: "AI Code Scanner - Made by Lisa_NS"
|
||||
}
|
||||
};
|
||||
|
||||
const payload = {
|
||||
embeds: [embed]
|
||||
};
|
||||
|
||||
try {
|
||||
await axios.post(webhook.discord.url, payload);
|
||||
console.log(`[INFO] Results sent to Discord webhook for ${filePath}`);
|
||||
} catch (error) {
|
||||
console.error(`[ERROR] Error sending to Discord webhook: ${error.response ? error.response.data : error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function requestedScan(filePath, messageHistoryId, currentFilePath) {
|
||||
if (filePath === currentFilePath) {
|
||||
console.log(`[INFO] Skipping scan for the same file: ${filePath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scannedFilesMap.has(messageHistoryId)) {
|
||||
scannedFilesMap.set(messageHistoryId, new Set());
|
||||
}
|
||||
const scannedFiles = scannedFilesMap.get(messageHistoryId);
|
||||
|
||||
if (scannedFiles.has(filePath)) {
|
||||
console.log(`[INFO] Skipping already scanned file for this message history: ${filePath}`);
|
||||
return;
|
||||
}
|
||||
|
||||
scannedFiles.add(filePath);
|
||||
|
||||
fs.readFile(filePath, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
console.error(`[ERROR] Error reading file: ${err}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const directoryContent = getDirectoryContent(path.dirname(filePath));
|
||||
const messageHistory = messageHistories[messageHistoryId] || [];
|
||||
|
||||
let conversationHistory = [
|
||||
{
|
||||
role: "system",
|
||||
content: `${aiprompt}`
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: `FILEPATH: ${filePath}, FILE: ${data}, DIRECTORY CONTENT: ${directoryContent}`
|
||||
}
|
||||
];
|
||||
|
||||
messageHistory.forEach(response => {
|
||||
conversationHistory.push({
|
||||
role: "assistant",
|
||||
content: response
|
||||
});
|
||||
conversationHistory.push({
|
||||
role: "user",
|
||||
content: `FILEPATH: ${filePath}, FILE: ${data}, DIRECTORY CONTENT: ${directoryContent}`
|
||||
});
|
||||
});
|
||||
|
||||
const apiUrl = `${llamaendpoint}`;
|
||||
let input = {
|
||||
"model": `${aimodel}`,
|
||||
"messages": conversationHistory,
|
||||
"stream": false,
|
||||
"raw": true
|
||||
};
|
||||
|
||||
axios.post(apiUrl, input)
|
||||
.then(response => {
|
||||
const aiResult = response.data.message.content;
|
||||
console.log(`[INFO] AI scan results for ${filePath}:\n${aiResult}`);
|
||||
|
||||
const patternFound = { found: false, value: "" };
|
||||
const extensionFound = { found: false, value: "" };
|
||||
const filenameFound = { found: false, value: "" };
|
||||
|
||||
sendReport(filePath, aiResult, patternFound, extensionFound, filenameFound);
|
||||
|
||||
messageHistories[messageHistoryId] = messageHistories[messageHistoryId] || [];
|
||||
messageHistories[messageHistoryId].push(aiResult);
|
||||
|
||||
const additionalScanFiles = extractAdditionalScanFiles(aiResult, path.dirname(filePath));
|
||||
additionalScanFiles.forEach(scanFilePath => {
|
||||
requestedScan(scanFilePath, messageHistoryId, filePath);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`[ERROR] Error running AI scan: ${error.response ? error.response.data : error.message}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function generateMessageHistoryId(filePath) {
|
||||
return `${filePath}-${Date.now()}`;
|
||||
}
|
||||
|
||||
scanDirectory(pathtoscan);
|
Loading…
x
Reference in New Issue
Block a user