Skip to content

Commit

Permalink
Merge pull request #2 from acuciureanu/1-handle-obfuscated-javascript
Browse files Browse the repository at this point in the history
Handle obfuscated/bundled/minified javascript
  • Loading branch information
acuciureanu authored Mar 16, 2024
2 parents 8bf8138 + 60520c1 commit bd5654e
Show file tree
Hide file tree
Showing 17 changed files with 1,345 additions and 1,341 deletions.
98 changes: 53 additions & 45 deletions App.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,65 @@
import AnalysisService from "./services/AnalysisService";
import chalk from "chalk";
import os from "os";
import fs from "fs";
import path from "path";
import MatchingRule from "./rules/MatchingRule";
import { urlPattern } from "./patterns/UrlPatterns";
import { secretsPatterns } from "./patterns/SecretsPatterns";
import MatchingRule from "./rules/MatchingRule";
import AnalysisService from "./services/AnalysisService";
import UnpackingService from "./services/UnpackingService";
import type { AnalysisResult } from "./models/AnalysisResult";
import { parseArgs, usage } from "./utils/CliUtil";

function usage() {
let toolName = path.basename(process.argv[1]);
const matchingRules: MatchingRule[] = [
new MatchingRule("endpoints", urlPattern),
new MatchingRule("secrets", secretsPatterns),
];

if (toolName.endsWith('.ts')) {
toolName = `bun run ${toolName}`;
} else {
if (os.platform() === 'win32' && !toolName.endsWith('.exe')) {
toolName += '.exe';
}
if (os.platform() !== 'win32') {
toolName = `./${toolName}`;
}
}
async function performAnalysis(
targetPath: string,
outputDirPath: string
): Promise<void> {
const analysisService = new AnalysisService(targetPath);
try {
const results: AnalysisResult[] = await analysisService.run(matchingRules);
console.log(results);

console.log(chalk.green(`Usage: ${toolName} <target-folder-or-file>`));
console.log();
console.log(`\nExamples:`);
console.log(` 1. Analyze a single JS file: ${chalk.yellow('📄')}`);
console.log(chalk.blue(` ${toolName} ./path/to/file.js`));
console.log(` This will analyze the file 'file.js' and output the report.`);
console.log();
console.log(
` 2. Analyze all TypeScript files in a directory (including subdirectories): ${chalk.yellow('📁')}`
);
console.log(chalk.blue(` ${toolName} ./path/to/directory`));
console.log(` This will analyze all JS files in the 'directory' and its subdirectories, and output a combined report.`);
console.log();
console.log(chalk.red(`Note: Make sure the path is correct. If the file or directory does not exist, the tool will not be able to analyze it.`));
}
if (!fs.existsSync(outputDirPath)) {
fs.mkdirSync(outputDirPath, { recursive: true });
}

if (
!process.argv[2] ||
process.argv.includes("-h") ||
process.argv.includes("--help")
) {
usage();
process.exit(0);
const outputPath = path.join(outputDirPath, "analysisResults.json");
fs.writeFileSync(outputPath, JSON.stringify(results, null, 2), "utf8");
} catch (error) {
console.error("An error occurred during analysis:", error);
}
}

const targetPath = process.argv[2];
async function main() {
const { targetPath, unpack, deobfuscate, unminify, unpackOutputDirPath } =
parseArgs(process.argv.slice(2));

const analysisService = new AnalysisService(targetPath);

const urlMatchingRule = new MatchingRule("endpoints", urlPattern);
const secretsMatchingRule = new MatchingRule("secrets", secretsPatterns);
if (!targetPath) {
usage();
return;
}

const results = analysisService.run([urlMatchingRule, secretsMatchingRule]);
try {
if (unpack || deobfuscate || unminify) {
const unpackingService = new UnpackingService(
targetPath,
unpackOutputDirPath
);
const unpackedPath = await unpackingService.unpack({
unpack,
deobfuscate,
unminify,
});
await performAnalysis(unpackedPath, unpackOutputDirPath);
} else {
await performAnalysis(targetPath, "unpacked");
}
} catch (error) {
console.error("An error occurred in main:", error);
}
}

console.log(results);
main();
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# js-maid 🕵️ - Your JavaScript Investigator

⚠️ This tool doesn't handle obfuscated or packed Javascript files ⚠️

js-maid started off as an idea to break down JavaScript into an AST (something like a blueprint) and then run some smart checks on it. It's a handy little tool for folks diving into code, looking for bugs, or just trying to get a grip on what's happening under the hood.

## Install Bun.sh
Expand All @@ -11,6 +9,7 @@ Check this out: [Bun.sh installation instructions](https://bun.sh/docs/installat
## Features 🌟

- **In-depth Analysis**: Thorough investigation of JavaScript code to identify patterns and potential vulnerabilities.
- **Debundling**: Debundling, deobfuscation, unminifying through [webcrack](https://github.com/j4k0xb/webcrack)
- **Custom Rules**: Utilize predefined rules to enhance your code investigation.
- **Regex Guide**: Leverage detailed regex patterns to pinpoint specific code structures.

Expand All @@ -19,7 +18,7 @@ Check this out: [Bun.sh installation instructions](https://bun.sh/docs/installat
Get started with js-maid by installing the necessary dependencies:

```bash
bun install
npm install
```

## Running js-maid 🏃
Expand Down
Binary file modified bun.lockb
Binary file not shown.
4 changes: 2 additions & 2 deletions datasources/FileDatasource.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as FileExtensions from "../constants/FileExtensions";
import fs from "fs";
import path from "path";
import type * as IDatasource from "../interfaces/IDatasource";
import type * as IDatasource from "../interfaces/Datasource";

export default class FileDatasource implements IDatasource.IDatasource {
export default class FileDatasource implements IDatasource.Datasource {
loadFiles(input: string): {
path: string; filePath: string; fileContent: string
}[] {
Expand Down
6 changes: 3 additions & 3 deletions engines/RuleEngine.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ProcessingContext } from "../contexts/ProcessingContext";
import type { IRule } from "../interfaces/IRule";
import type { Rule } from "../interfaces/Rule";
import type MatchingRule from "../rules/MatchingRule";

export default class RuleEngine {
private rules: IRule[] = [];
private rules: Rule[] = [];

constructor(private ast: any) {}

addRule(rule: IRule): RuleEngine {
addRule(rule: Rule): RuleEngine {
this.rules.push(rule);
return this;
}
Expand Down
2 changes: 1 addition & 1 deletion interfaces/IDatasource.ts → interfaces/Datasource.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Represents a datasource that can load files.
*/
export interface IDatasource {
export interface Datasource {
/**
* Loads files based on the provided input.
* @param input - The input to use for loading files.
Expand Down
2 changes: 1 addition & 1 deletion interfaces/IRule.ts → interfaces/Rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type MatchingRule from "../rules/MatchingRule";
/**
* Represents a rule that can be applied to a node.
*/
export interface IRule {
export interface Rule {
/**
* Applies the rule to the given node.
* @param node The node to apply the rule to.
Expand Down
18 changes: 18 additions & 0 deletions models/AnalysisResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Represents the result of an analysis.
*/
export interface AnalysisResult {
/**
* The filename of the analyzed file.
*/
filename: string;

/**
* The matches found during the analysis.
* Each rule type is mapped to an array of matching strings.
*/
matches: {
[ruleType: string]: string[];
};
}

Loading

0 comments on commit bd5654e

Please sign in to comment.