@fortressjs/cli 0.1.2 → 0.1.4
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/dist/ast-scanner.d.ts +5 -0
- package/dist/ast-scanner.js +58 -0
- package/dist/ast-scanner.test.d.ts +1 -0
- package/dist/ast-scanner.test.js +24 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +45 -10
- package/dist/report-generator.d.ts +8 -0
- package/dist/report-generator.js +32 -0
- package/package.json +48 -47
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ASTScanner = void 0;
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
class ASTScanner {
|
|
9
|
+
name = "ASTScanner";
|
|
10
|
+
scan(fileContent) {
|
|
11
|
+
const sourceFile = typescript_1.default.createSourceFile("temp.ts", fileContent, typescript_1.default.ScriptTarget.Latest, true);
|
|
12
|
+
const result = {
|
|
13
|
+
hasCSP: false,
|
|
14
|
+
hasRateLimiting: false,
|
|
15
|
+
hasRequestSizeLimiting: false,
|
|
16
|
+
hasLogger: false,
|
|
17
|
+
hasThreatDetection: false,
|
|
18
|
+
hasHTTPS: false
|
|
19
|
+
};
|
|
20
|
+
const visit = (node) => {
|
|
21
|
+
if (typescript_1.default.isImportDeclaration(node)) {
|
|
22
|
+
const moduleName = node.moduleSpecifier.getText(sourceFile);
|
|
23
|
+
if (moduleName.includes("@fortressjs/core")) {
|
|
24
|
+
result.hasCSP = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (typescript_1.default.isCallExpression(node)) {
|
|
28
|
+
const expression = node.expression;
|
|
29
|
+
if (typescript_1.default.isPropertyAccessExpression(expression)) {
|
|
30
|
+
if (expression.expression.getText(sourceFile) ===
|
|
31
|
+
"fortress") {
|
|
32
|
+
switch (expression.name.getText(sourceFile)) {
|
|
33
|
+
case "headers":
|
|
34
|
+
result.hasCSP = true;
|
|
35
|
+
break;
|
|
36
|
+
case "rateLimit":
|
|
37
|
+
result.hasRateLimiting = true;
|
|
38
|
+
break;
|
|
39
|
+
case "requestLimit":
|
|
40
|
+
result.hasRequestSizeLimiting = true;
|
|
41
|
+
break;
|
|
42
|
+
case "logger":
|
|
43
|
+
result.hasLogger = true;
|
|
44
|
+
break;
|
|
45
|
+
case "threatDetector":
|
|
46
|
+
result.hasThreatDetection = true;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
typescript_1.default.forEachChild(node, visit);
|
|
53
|
+
};
|
|
54
|
+
visit(sourceFile);
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.ASTScanner = ASTScanner;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ast_scanner_1 = require("./ast-scanner");
|
|
4
|
+
describe("ASTScanner", () => {
|
|
5
|
+
it("should parse valid code", () => {
|
|
6
|
+
const scanner = new ast_scanner_1.ASTScanner();
|
|
7
|
+
const result = scanner.scan(`
|
|
8
|
+
import express from "express";
|
|
9
|
+
|
|
10
|
+
const app = express();
|
|
11
|
+
|
|
12
|
+
app.get("/", () => {});
|
|
13
|
+
`);
|
|
14
|
+
expect(result).toBeDefined();
|
|
15
|
+
});
|
|
16
|
+
it("should detect fortress imports", () => {
|
|
17
|
+
const scanner = new ast_scanner_1.ASTScanner();
|
|
18
|
+
const result = scanner.scan(`
|
|
19
|
+
import fortress
|
|
20
|
+
from "@fortressjs/core";
|
|
21
|
+
`);
|
|
22
|
+
expect(result.hasCSP).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function runAudit(targetPath?: string): void;
|
|
1
|
+
export declare function runAudit(targetPath?: string, jsonOutput?: boolean, reportOutput?: boolean): void;
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.runAudit = runAudit;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const
|
|
9
|
+
const report_generator_1 = require("./report-generator");
|
|
10
|
+
const ast_scanner_1 = require("./ast-scanner");
|
|
10
11
|
// ANSI Terminal Colors
|
|
11
12
|
const C = {
|
|
12
13
|
reset: "\x1b[0m",
|
|
@@ -58,9 +59,7 @@ function getFilesRecursively(dir, fileList = []) {
|
|
|
58
59
|
}
|
|
59
60
|
return fileList;
|
|
60
61
|
}
|
|
61
|
-
function runAudit(targetPath) {
|
|
62
|
-
console.log(`\n${C.bold}${C.cyan}🛡️ FortressJS Security Audit CLI${C.reset}`);
|
|
63
|
-
console.log(`${C.dim}=========================================${C.reset}\n`);
|
|
62
|
+
function runAudit(targetPath, jsonOutput = false, reportOutput = false) {
|
|
64
63
|
const target = path_1.default.resolve(targetPath || process.cwd());
|
|
65
64
|
let files;
|
|
66
65
|
try {
|
|
@@ -71,12 +70,26 @@ function runAudit(targetPath) {
|
|
|
71
70
|
process.exit(1);
|
|
72
71
|
}
|
|
73
72
|
if (files.length === 0) {
|
|
73
|
+
if (jsonOutput) {
|
|
74
|
+
console.log(JSON.stringify({
|
|
75
|
+
target,
|
|
76
|
+
score: 0,
|
|
77
|
+
missing: [],
|
|
78
|
+
recommendations: [],
|
|
79
|
+
message: "No JavaScript or TypeScript files found"
|
|
80
|
+
}, null, 2));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
74
83
|
console.log(`${C.yellow}No JavaScript or TypeScript files found to scan in: ${target}${C.reset}`);
|
|
75
84
|
return;
|
|
76
85
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
if (!jsonOutput && !reportOutput) {
|
|
87
|
+
console.log(`\n${C.bold}${C.cyan}🛡️ FortressJS Security Audit CLI${C.reset}`);
|
|
88
|
+
console.log(`${C.dim}=========================================${C.reset}\n`);
|
|
89
|
+
console.log(`${C.dim}Target: ${target}${C.reset}`);
|
|
90
|
+
console.log(`${C.dim}Scanning ${files.length} file(s)...${C.reset}\n`);
|
|
91
|
+
}
|
|
92
|
+
const scanner = new ast_scanner_1.ASTScanner();
|
|
80
93
|
// Aggregate results across all files in the project
|
|
81
94
|
const aggregated = {
|
|
82
95
|
hasCSP: false,
|
|
@@ -142,6 +155,22 @@ function runAudit(targetPath) {
|
|
|
142
155
|
recommendations.push("Configure fortress.headers({ strictTransportSecurity: ... }) for HSTS");
|
|
143
156
|
}
|
|
144
157
|
score = Math.max(0, score);
|
|
158
|
+
const auditResult = {
|
|
159
|
+
target,
|
|
160
|
+
score,
|
|
161
|
+
missing,
|
|
162
|
+
recommendations
|
|
163
|
+
};
|
|
164
|
+
if (reportOutput) {
|
|
165
|
+
const markdown = (0, report_generator_1.generateMarkdownReport)(auditResult);
|
|
166
|
+
fs_1.default.writeFileSync("fortress-report.md", markdown);
|
|
167
|
+
console.log("Report generated: fortress-report.md");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (jsonOutput) {
|
|
171
|
+
console.log(JSON.stringify(auditResult, null, 2));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
145
174
|
// Pick color based on score
|
|
146
175
|
let scoreColor = C.red;
|
|
147
176
|
if (score >= 90)
|
|
@@ -166,8 +195,12 @@ function runAudit(targetPath) {
|
|
|
166
195
|
// CLI entry point
|
|
167
196
|
const args = process.argv.slice(2);
|
|
168
197
|
if (args[0] === "audit") {
|
|
169
|
-
const
|
|
170
|
-
|
|
198
|
+
const jsonOutput = args.includes("--json");
|
|
199
|
+
const reportOutput = args.includes("--report");
|
|
200
|
+
const target = args.find((arg) => arg !== "audit" &&
|
|
201
|
+
arg !== "--json" &&
|
|
202
|
+
arg !== "--report");
|
|
203
|
+
runAudit(target, jsonOutput, reportOutput);
|
|
171
204
|
}
|
|
172
205
|
else {
|
|
173
206
|
console.log(`\n${C.bold}FortressJS CLI${C.reset}`);
|
|
@@ -175,5 +208,7 @@ else {
|
|
|
175
208
|
console.log(` fortress audit`);
|
|
176
209
|
console.log(` fortress audit .`);
|
|
177
210
|
console.log(` fortress audit ./src`);
|
|
178
|
-
console.log(` fortress audit ./src/app.ts
|
|
211
|
+
console.log(` fortress audit ./src/app.ts`);
|
|
212
|
+
console.log(` fortress audit --json`);
|
|
213
|
+
console.log(` fortress audit --report\n`);
|
|
179
214
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateMarkdownReport = generateMarkdownReport;
|
|
4
|
+
function generateMarkdownReport(auditResult) {
|
|
5
|
+
const date = new Date()
|
|
6
|
+
.toISOString()
|
|
7
|
+
.split("T")[0];
|
|
8
|
+
return `# FortressJS Security Report
|
|
9
|
+
|
|
10
|
+
Generated: ${date}
|
|
11
|
+
|
|
12
|
+
## Target
|
|
13
|
+
|
|
14
|
+
${auditResult.target}
|
|
15
|
+
|
|
16
|
+
## Security Score
|
|
17
|
+
|
|
18
|
+
${auditResult.score}/100
|
|
19
|
+
|
|
20
|
+
## Missing Protections
|
|
21
|
+
|
|
22
|
+
${auditResult.missing
|
|
23
|
+
.map(item => `- ${item}`)
|
|
24
|
+
.join("\n")}
|
|
25
|
+
|
|
26
|
+
## Recommendations
|
|
27
|
+
|
|
28
|
+
${auditResult.recommendations
|
|
29
|
+
.map(item => `- ${item}`)
|
|
30
|
+
.join("\n")}
|
|
31
|
+
`;
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,47 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@fortressjs/cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Security audit CLI for Express applications. Detect missing security headers, rate limits, request validation, and threat protection.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Davanesh Saminathan",
|
|
7
|
-
"homepage": "https://github.com/davanesh/fortressjs",
|
|
8
|
-
"bugs": {
|
|
9
|
-
"url": "https://github.com/davanesh/fortressjs/issues"
|
|
10
|
-
},
|
|
11
|
-
"repository": {
|
|
12
|
-
"type": "git",
|
|
13
|
-
"url": "git+https://github.com/davanesh/fortressjs.git"
|
|
14
|
-
},
|
|
15
|
-
"keywords": [
|
|
16
|
-
"security",
|
|
17
|
-
"cli",
|
|
18
|
-
"audit",
|
|
19
|
-
"express",
|
|
20
|
-
"nodejs",
|
|
21
|
-
"middleware",
|
|
22
|
-
"api-security",
|
|
23
|
-
"fortressjs"
|
|
24
|
-
],
|
|
25
|
-
"engines": {
|
|
26
|
-
"node": ">=18"
|
|
27
|
-
},
|
|
28
|
-
"bin": {
|
|
29
|
-
"fortress": "bin/fortress.js"
|
|
30
|
-
},
|
|
31
|
-
"main": "dist/index.js",
|
|
32
|
-
"types": "dist/index.d.ts",
|
|
33
|
-
"files": [
|
|
34
|
-
"dist",
|
|
35
|
-
"bin",
|
|
36
|
-
"README.md"
|
|
37
|
-
],
|
|
38
|
-
"scripts": {
|
|
39
|
-
"build": "tsc"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@fortressjs/cli",
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "Security audit CLI for Express applications. Detect missing security headers, rate limits, request validation, and threat protection.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Davanesh Saminathan",
|
|
7
|
+
"homepage": "https://github.com/davanesh/fortressjs",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/davanesh/fortressjs/issues"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/davanesh/fortressjs.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"security",
|
|
17
|
+
"cli",
|
|
18
|
+
"audit",
|
|
19
|
+
"express",
|
|
20
|
+
"nodejs",
|
|
21
|
+
"middleware",
|
|
22
|
+
"api-security",
|
|
23
|
+
"fortressjs"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"fortress": "bin/fortress.js"
|
|
30
|
+
},
|
|
31
|
+
"main": "dist/index.js",
|
|
32
|
+
"types": "dist/index.d.ts",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"bin",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc",
|
|
40
|
+
"test": "jest"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@fortressjs/core": "^0.1.2"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"typescript": "^5.0.0"
|
|
47
|
+
}
|
|
48
|
+
}
|