@fortressjs/cli 0.1.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/bin/fortress.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +152 -0
- package/dist/scanner.d.ts +20 -0
- package/dist/scanner.js +35 -0
- package/package.json +46 -0
package/bin/fortress.js
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runAudit(): void;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
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.runAudit = runAudit;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const scanner_1 = require("./scanner");
|
|
10
|
+
// ANSI Terminal Colors
|
|
11
|
+
const C = {
|
|
12
|
+
reset: "\x1b[0m",
|
|
13
|
+
bold: "\x1b[1m",
|
|
14
|
+
dim: "\x1b[2m",
|
|
15
|
+
red: "\x1b[31m",
|
|
16
|
+
green: "\x1b[32m",
|
|
17
|
+
yellow: "\x1b[33m",
|
|
18
|
+
cyan: "\x1b[36m"
|
|
19
|
+
};
|
|
20
|
+
function getFilesRecursively(dir, fileList = []) {
|
|
21
|
+
if (!fs_1.default.existsSync(dir))
|
|
22
|
+
return fileList;
|
|
23
|
+
const files = fs_1.default.readdirSync(dir);
|
|
24
|
+
for (const file of files) {
|
|
25
|
+
const filePath = path_1.default.join(dir, file);
|
|
26
|
+
const stat = fs_1.default.statSync(filePath);
|
|
27
|
+
// Skip node_modules, build directories, hidden files, and coverage files
|
|
28
|
+
if (file === "node_modules" ||
|
|
29
|
+
file === "dist" ||
|
|
30
|
+
file === "build" ||
|
|
31
|
+
file === "coverage" ||
|
|
32
|
+
file.startsWith(".")) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (stat.isDirectory()) {
|
|
36
|
+
getFilesRecursively(filePath, fileList);
|
|
37
|
+
}
|
|
38
|
+
else if (filePath.endsWith(".ts") || filePath.endsWith(".js")) {
|
|
39
|
+
// Exclude definition files and config files
|
|
40
|
+
if (!filePath.endsWith(".d.ts") && !filePath.includes("config.js")) {
|
|
41
|
+
fileList.push(filePath);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return fileList;
|
|
46
|
+
}
|
|
47
|
+
function runAudit() {
|
|
48
|
+
console.log(`\n${C.bold}${C.cyan}🛡️ FortressJS Security Audit CLI${C.reset}`);
|
|
49
|
+
console.log(`${C.dim}=========================================${C.reset}\n`);
|
|
50
|
+
const cwd = process.cwd();
|
|
51
|
+
const files = getFilesRecursively(cwd);
|
|
52
|
+
if (files.length === 0) {
|
|
53
|
+
console.log(`${C.yellow}No JavaScript or TypeScript files found to scan in: ${cwd}${C.reset}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
console.log(`${C.dim}Scanning ${files.length} file(s)...${C.reset}`);
|
|
57
|
+
const scanner = new scanner_1.RegexScanner();
|
|
58
|
+
// Aggregate results across all files in the project
|
|
59
|
+
const aggregated = {
|
|
60
|
+
hasCSP: false,
|
|
61
|
+
hasRateLimiting: false,
|
|
62
|
+
hasRequestSizeLimiting: false,
|
|
63
|
+
hasLogger: false,
|
|
64
|
+
hasThreatDetection: false,
|
|
65
|
+
hasHTTPS: false
|
|
66
|
+
};
|
|
67
|
+
for (const file of files) {
|
|
68
|
+
try {
|
|
69
|
+
const content = fs_1.default.readFileSync(file, "utf8");
|
|
70
|
+
const result = scanner.scan(content);
|
|
71
|
+
if (result.hasCSP)
|
|
72
|
+
aggregated.hasCSP = true;
|
|
73
|
+
if (result.hasRateLimiting)
|
|
74
|
+
aggregated.hasRateLimiting = true;
|
|
75
|
+
if (result.hasRequestSizeLimiting)
|
|
76
|
+
aggregated.hasRequestSizeLimiting = true;
|
|
77
|
+
if (result.hasLogger)
|
|
78
|
+
aggregated.hasLogger = true;
|
|
79
|
+
if (result.hasThreatDetection)
|
|
80
|
+
aggregated.hasThreatDetection = true;
|
|
81
|
+
if (result.hasHTTPS)
|
|
82
|
+
aggregated.hasHTTPS = true;
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
console.error(`${C.red}Error reading file ${file}: ${e.message}${C.reset}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Calculate score
|
|
89
|
+
let score = 100;
|
|
90
|
+
const missing = [];
|
|
91
|
+
const recommendations = [];
|
|
92
|
+
if (!aggregated.hasCSP) {
|
|
93
|
+
score -= 15;
|
|
94
|
+
missing.push("Content Security Policy (CSP)");
|
|
95
|
+
recommendations.push("Add fortress.headers() to configure secure CSP and other headers");
|
|
96
|
+
}
|
|
97
|
+
if (!aggregated.hasRateLimiting) {
|
|
98
|
+
score -= 20;
|
|
99
|
+
missing.push("Rate Limiting");
|
|
100
|
+
recommendations.push("Add fortress.rateLimit() to prevent DDoS and Brute Force attacks");
|
|
101
|
+
}
|
|
102
|
+
if (!aggregated.hasRequestSizeLimiting) {
|
|
103
|
+
score -= 15;
|
|
104
|
+
missing.push("Request Size Limiting");
|
|
105
|
+
recommendations.push("Add fortress.requestLimit() to restrict payload sizes");
|
|
106
|
+
}
|
|
107
|
+
if (!aggregated.hasLogger) {
|
|
108
|
+
score -= 15;
|
|
109
|
+
missing.push("Security Logger");
|
|
110
|
+
recommendations.push("Add fortress.logger() to record requests in event store");
|
|
111
|
+
}
|
|
112
|
+
if (!aggregated.hasThreatDetection) {
|
|
113
|
+
score -= 20;
|
|
114
|
+
missing.push("Threat Intelligence Engine");
|
|
115
|
+
recommendations.push("Add fortress.threatDetector() to intercept automated scans and brute force");
|
|
116
|
+
}
|
|
117
|
+
if (!aggregated.hasHTTPS) {
|
|
118
|
+
score -= 15;
|
|
119
|
+
missing.push("HTTPS Enforcement / HSTS");
|
|
120
|
+
recommendations.push("Configure fortress.headers({ strictTransportSecurity: ... }) for HSTS");
|
|
121
|
+
}
|
|
122
|
+
score = Math.max(0, score);
|
|
123
|
+
// Pick color based on score
|
|
124
|
+
let scoreColor = C.red;
|
|
125
|
+
if (score >= 90)
|
|
126
|
+
scoreColor = C.green;
|
|
127
|
+
else if (score >= 70)
|
|
128
|
+
scoreColor = C.yellow;
|
|
129
|
+
console.log(`${C.bold}Security Score: ${scoreColor}${score}/100${C.reset}\n`);
|
|
130
|
+
if (missing.length === 0) {
|
|
131
|
+
console.log(`${C.bold}${C.green}🎉 Congratulations! Perfect score. Your application follows core FortressJS security best practices.${C.reset}\n`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
console.log(`${C.bold}${C.red}Missing Protections:${C.reset}`);
|
|
135
|
+
for (const item of missing) {
|
|
136
|
+
console.log(` ${C.red}✗${C.reset} ${item}`);
|
|
137
|
+
}
|
|
138
|
+
console.log(`\n${C.bold}${C.yellow}Recommendations:${C.reset}`);
|
|
139
|
+
for (const rec of recommendations) {
|
|
140
|
+
console.log(` ${C.cyan}•${C.reset} ${rec}`);
|
|
141
|
+
}
|
|
142
|
+
console.log("");
|
|
143
|
+
}
|
|
144
|
+
// CLI entry point
|
|
145
|
+
const args = process.argv.slice(2);
|
|
146
|
+
if (args[0] === "audit") {
|
|
147
|
+
runAudit();
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
console.log(`\n${C.bold}FortressJS CLI${C.reset}`);
|
|
151
|
+
console.log(`${C.dim}Usage: npx fortress audit${C.reset}\n`);
|
|
152
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ScanResult {
|
|
2
|
+
hasCSP: boolean;
|
|
3
|
+
hasRateLimiting: boolean;
|
|
4
|
+
hasRequestSizeLimiting: boolean;
|
|
5
|
+
hasLogger: boolean;
|
|
6
|
+
hasThreatDetection: boolean;
|
|
7
|
+
hasHTTPS: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface AuditScanner {
|
|
10
|
+
name: string;
|
|
11
|
+
scan(fileContent: string): ScanResult;
|
|
12
|
+
}
|
|
13
|
+
export declare class RegexScanner implements AuditScanner {
|
|
14
|
+
name: string;
|
|
15
|
+
scan(fileContent: string): ScanResult;
|
|
16
|
+
}
|
|
17
|
+
export declare class ASTScanner implements AuditScanner {
|
|
18
|
+
name: string;
|
|
19
|
+
scan(fileContent: string): ScanResult;
|
|
20
|
+
}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ASTScanner = exports.RegexScanner = void 0;
|
|
4
|
+
// Concrete implementation of Regex-based scanner for MVP
|
|
5
|
+
class RegexScanner {
|
|
6
|
+
name = "RegexScanner";
|
|
7
|
+
scan(fileContent) {
|
|
8
|
+
const hasCSP = /fortress\.headers|helmet|Content-Security-Policy/i.test(fileContent);
|
|
9
|
+
const hasRateLimiting = /fortress\.rateLimit|express-rate-limit/i.test(fileContent);
|
|
10
|
+
const hasRequestSizeLimiting = /fortress\.requestLimit|limit\s*:\s*['"]\d+m?k?b['"]/i.test(fileContent);
|
|
11
|
+
const hasLogger = /fortress\.logger|morgan|winston/i.test(fileContent);
|
|
12
|
+
const hasThreatDetection = /fortress\.threatDetector/i.test(fileContent);
|
|
13
|
+
const hasHTTPS = /trust proxy|Strict-Transport-Security|https/i.test(fileContent);
|
|
14
|
+
return {
|
|
15
|
+
hasCSP,
|
|
16
|
+
hasRateLimiting,
|
|
17
|
+
hasRequestSizeLimiting,
|
|
18
|
+
hasLogger,
|
|
19
|
+
hasThreatDetection,
|
|
20
|
+
hasHTTPS
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.RegexScanner = RegexScanner;
|
|
25
|
+
// Future AST-based scanner abstraction skeleton
|
|
26
|
+
class ASTScanner {
|
|
27
|
+
name = "ASTScanner (Future)";
|
|
28
|
+
scan(fileContent) {
|
|
29
|
+
// In v2, this scanner will parse the TypeScript/JavaScript code into an AST
|
|
30
|
+
// (using e.g., @babel/parser, typescript or acorn) and perform structural analysis
|
|
31
|
+
// to identify secure middleware configurations accurately without false positives.
|
|
32
|
+
throw new Error("AST Scanning is not implemented yet. Using RegexScanner as fallback.");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.ASTScanner = ASTScanner;
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fortressjs/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Security auditing CLI for FortressJS and Express applications",
|
|
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
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@fortressjs/core": "0.1.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"typescript": "^5.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|