fortly-cli 1.0.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/LICENSE +21 -0
- package/README.md +195 -0
- package/bin/fortly.js +2 -0
- package/dist/api/client.d.ts +10 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +39 -0
- package/dist/api/client.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +40 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +43 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/report.d.ts +2 -0
- package/dist/commands/report.d.ts.map +1 -0
- package/dist/commands/report.js +25 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/scan.d.ts +2 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +127 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/config/store.d.ts +9 -0
- package/dist/config/store.d.ts.map +1 -0
- package/dist/config/store.js +27 -0
- package/dist/config/store.js.map +1 -0
- package/dist/output/ci.d.ts +6 -0
- package/dist/output/ci.d.ts.map +1 -0
- package/dist/output/ci.js +39 -0
- package/dist/output/ci.js.map +1 -0
- package/dist/output/sarif.d.ts +21 -0
- package/dist/output/sarif.d.ts.map +1 -0
- package/dist/output/sarif.js +54 -0
- package/dist/output/sarif.js.map +1 -0
- package/dist/output/terminal.d.ts +33 -0
- package/dist/output/terminal.d.ts.map +1 -0
- package/dist/output/terminal.js +130 -0
- package/dist/output/terminal.js.map +1 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fortly
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# fortly
|
|
2
|
+
|
|
3
|
+
Automated web security scanner with AI-powered vulnerability detection.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/fortly)
|
|
6
|
+
[](https://github.com/fortly/fortly-cli/blob/main/LICENSE)
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
|
|
9
|
+
> Requires Node.js >= 18
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Run without installing
|
|
17
|
+
npx fortly scan https://example.com
|
|
18
|
+
|
|
19
|
+
# Install globally
|
|
20
|
+
npm install -g fortly
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Basic scan
|
|
29
|
+
fortly scan https://myapp.com
|
|
30
|
+
|
|
31
|
+
# Scan with options
|
|
32
|
+
fortly scan https://myapp.com --depth 5 --format html --output ./reports
|
|
33
|
+
|
|
34
|
+
# Cloud mode (unlimited scans)
|
|
35
|
+
fortly config set api-key ft_sk_your_key_here
|
|
36
|
+
fortly scan https://myapp.com --cloud
|
|
37
|
+
|
|
38
|
+
# CI/CD mode (exit code 1 if score below threshold)
|
|
39
|
+
fortly scan https://staging.myapp.com --ci --fail-threshold 70
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
### `fortly scan <target>`
|
|
47
|
+
|
|
48
|
+
Run a security scan against a target URL.
|
|
49
|
+
|
|
50
|
+
| Option | Type | Default | Description |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| `--depth <n>` | number | `3` | Maximum crawl depth |
|
|
53
|
+
| `--timeout <ms>` | number | `30000` | Request timeout in milliseconds |
|
|
54
|
+
| `--threads <n>` | number | `10` | Number of concurrent threads |
|
|
55
|
+
| `--format <type>` | string | `terminal` | Output format (`terminal`, `json`, `html`, `both`, `sarif`) |
|
|
56
|
+
| `--output <path>` | string | `./` | Directory for saved reports |
|
|
57
|
+
| `--fail-threshold <n>` | number | `0` | Minimum score to pass (0-100). Exit code 1 if below |
|
|
58
|
+
| `--cloud` | boolean | `false` | Use cloud engine for unlimited scans |
|
|
59
|
+
| `--api-key <key>` | string | — | API key for cloud mode (overrides config) |
|
|
60
|
+
| `--no-ai` | boolean | `false` | Disable AI-powered vulnerability detection |
|
|
61
|
+
| `--ci` | boolean | `false` | CI-friendly output (no color, no interactive prompts) |
|
|
62
|
+
| `--verbose` | boolean | `false` | Enable verbose logging |
|
|
63
|
+
|
|
64
|
+
### `fortly report <path>`
|
|
65
|
+
|
|
66
|
+
View a previously saved report file in the terminal.
|
|
67
|
+
|
|
68
|
+
### `fortly config <action> [key] [value]`
|
|
69
|
+
|
|
70
|
+
Manage CLI configuration.
|
|
71
|
+
|
|
72
|
+
| Action | Description |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `set <key> <value>` | Set a configuration value |
|
|
75
|
+
| `get <key>` | Get a configuration value |
|
|
76
|
+
| `list` | List all configuration values |
|
|
77
|
+
| `reset` | Reset configuration to defaults |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Output Formats
|
|
82
|
+
|
|
83
|
+
| Format | Description |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `terminal` | Colored output with tables (default) |
|
|
86
|
+
| `json` | Machine-readable JSON |
|
|
87
|
+
| `html` | Full HTML report with charts and details |
|
|
88
|
+
| `both` | JSON + HTML |
|
|
89
|
+
| `sarif` | SARIF 2.1.0 for GitHub Code Scanning |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## CI/CD Integration
|
|
94
|
+
|
|
95
|
+
### GitHub Actions
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
- name: Run Fortly
|
|
99
|
+
run: |
|
|
100
|
+
npx fortly scan ${{ secrets.TARGET_URL }} \
|
|
101
|
+
--ci \
|
|
102
|
+
--fail-threshold 70 \
|
|
103
|
+
--format sarif \
|
|
104
|
+
--output ./results
|
|
105
|
+
|
|
106
|
+
- name: Upload SARIF
|
|
107
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
108
|
+
with:
|
|
109
|
+
sarif_file: ./results
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### GitLab CI
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
security_scan:
|
|
116
|
+
image: node:20
|
|
117
|
+
script:
|
|
118
|
+
- npx fortly scan $TARGET_URL --ci --fail-threshold 70 --format json --output ./results
|
|
119
|
+
artifacts:
|
|
120
|
+
paths:
|
|
121
|
+
- results/
|
|
122
|
+
when: always
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Azure Pipelines
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
- script: |
|
|
129
|
+
npx fortly scan $(TARGET_URL) --ci --fail-threshold 70 --format json --output $(Build.ArtifactStagingDirectory)/fortly
|
|
130
|
+
displayName: "Run Fortly Scan"
|
|
131
|
+
|
|
132
|
+
- publish: $(Build.ArtifactStagingDirectory)/fortly
|
|
133
|
+
artifact: fortly-report
|
|
134
|
+
condition: always()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Jenkins
|
|
138
|
+
|
|
139
|
+
```groovy
|
|
140
|
+
stage('Security Scan') {
|
|
141
|
+
steps {
|
|
142
|
+
sh 'npx fortly scan $TARGET_URL --ci --fail-threshold 70 --format json --output ./fortly-report'
|
|
143
|
+
}
|
|
144
|
+
post {
|
|
145
|
+
always {
|
|
146
|
+
archiveArtifacts artifacts: 'fortly-report/**', allowEmptyArchive: true
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Exit Codes
|
|
155
|
+
|
|
156
|
+
| Code | Meaning |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `0` | Scan passed (score above threshold) |
|
|
159
|
+
| `1` | Scan failed (score below threshold or error) |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Configuration
|
|
164
|
+
|
|
165
|
+
Configuration is stored in `~/.config/fortly/`.
|
|
166
|
+
|
|
167
|
+
| Key | Description |
|
|
168
|
+
|---|---|
|
|
169
|
+
| `api-key` | API key for cloud mode |
|
|
170
|
+
| `default-threshold` | Default fail threshold for scans |
|
|
171
|
+
| `default-format` | Default output format |
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
fortly config set api-key ft_sk_your_key_here
|
|
175
|
+
fortly config set default-threshold 70
|
|
176
|
+
fortly config set default-format json
|
|
177
|
+
fortly config list
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Features
|
|
183
|
+
|
|
184
|
+
- **15 security scanners** covering a wide range of vulnerability classes
|
|
185
|
+
- **AI-powered vulnerability detection** for reduced false positives
|
|
186
|
+
- **OWASP Top 10 + API Top 10** full coverage
|
|
187
|
+
- **IaC scanning** for Terraform, Dockerfile, and Kubernetes manifests
|
|
188
|
+
- **Compliance mapping** to SOC2, ISO 27001, PCI DSS, HIPAA, and NIST frameworks
|
|
189
|
+
- **SARIF output** for native GitHub Code Scanning integration
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
[MIT](./LICENSE)
|
package/bin/fortly.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class CloudClient {
|
|
2
|
+
private http;
|
|
3
|
+
constructor(baseUrl: string, apiKey: string);
|
|
4
|
+
createScan(targetUrl: string): Promise<{
|
|
5
|
+
scanId: string;
|
|
6
|
+
}>;
|
|
7
|
+
getScan(scanId: string): Promise<any>;
|
|
8
|
+
waitForCompletion(scanId: string, timeoutSeconds: number): Promise<any>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAEA,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAgB;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAQrC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAK1D,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAKrC,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;CAU9E"}
|
|
@@ -0,0 +1,39 @@
|
|
|
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.CloudClient = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
class CloudClient {
|
|
9
|
+
http;
|
|
10
|
+
constructor(baseUrl, apiKey) {
|
|
11
|
+
this.http = axios_1.default.create({
|
|
12
|
+
baseURL: baseUrl,
|
|
13
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
14
|
+
timeout: 30000,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async createScan(targetUrl) {
|
|
18
|
+
const { data } = await this.http.post("/api/v2/scans", { targetUrl });
|
|
19
|
+
return data.data;
|
|
20
|
+
}
|
|
21
|
+
async getScan(scanId) {
|
|
22
|
+
const { data } = await this.http.get(`/api/v2/scans/${scanId}`);
|
|
23
|
+
return data.data;
|
|
24
|
+
}
|
|
25
|
+
async waitForCompletion(scanId, timeoutSeconds) {
|
|
26
|
+
const deadline = Date.now() + timeoutSeconds * 1000;
|
|
27
|
+
while (Date.now() < deadline) {
|
|
28
|
+
const scan = await this.getScan(scanId);
|
|
29
|
+
if (scan.status === "completed")
|
|
30
|
+
return scan;
|
|
31
|
+
if (scan.status === "failed")
|
|
32
|
+
throw new Error(`Scan failed: ${scan.error || "unknown"}`);
|
|
33
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Scan timed out after ${timeoutSeconds}s`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.CloudClient = CloudClient;
|
|
39
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA6C;AAE7C,MAAa,WAAW;IACd,IAAI,CAAgB;IAE5B,YAAY,OAAe,EAAE,MAAc;QACzC,IAAI,CAAC,IAAI,GAAG,eAAK,CAAC,MAAM,CAAC;YACvB,OAAO,EAAE,OAAO;YAChB,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAClF,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,SAAiB;QAChC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAc;QAC1B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,MAAM,EAAE,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,MAAc,EAAE,cAAsB;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,GAAG,IAAI,CAAC;QACpD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW;gBAAE,OAAO,IAAI,CAAC;YAC7C,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC,CAAC;YACzF,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9C,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,wBAAwB,cAAc,GAAG,CAAC,CAAC;IAC7D,CAAC;CACF;AA/BD,kCA+BC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const commander_1 = require("commander");
|
|
4
|
+
const scan_1 = require("./commands/scan");
|
|
5
|
+
const report_1 = require("./commands/report");
|
|
6
|
+
const config_1 = require("./commands/config");
|
|
7
|
+
const program = new commander_1.Command();
|
|
8
|
+
program
|
|
9
|
+
.name("fortly")
|
|
10
|
+
.description("Fortly CLI — Automated web security scanner")
|
|
11
|
+
.version("1.0.0");
|
|
12
|
+
program
|
|
13
|
+
.command("scan <target>")
|
|
14
|
+
.description("Scan a target URL for security vulnerabilities")
|
|
15
|
+
.option("-d, --depth <number>", "Maximum crawl depth", "3")
|
|
16
|
+
.option("-t, --timeout <number>", "Request timeout in ms", "10000")
|
|
17
|
+
.option("--threads <number>", "Concurrent requests", "5")
|
|
18
|
+
.option("-f, --format <type>", "Output format: html, json, both, terminal", "terminal")
|
|
19
|
+
.option("-o, --output <dir>", "Output directory for reports", "./fortly-reports")
|
|
20
|
+
.option("--fail-threshold <number>", "Exit with code 1 if score below threshold (for CI)")
|
|
21
|
+
.option("--cloud", "Use Fortly cloud API instead of local scan")
|
|
22
|
+
.option("--api-key <key>", "API key for cloud mode (or set FT_API_KEY env var)")
|
|
23
|
+
.option("--no-ai", "Disable AI-powered scanning")
|
|
24
|
+
.option("--ci", "CI mode: minimal output, JSON to stdout, exit codes")
|
|
25
|
+
.option("-v, --verbose", "Verbose output")
|
|
26
|
+
.action(scan_1.scanCommand);
|
|
27
|
+
program
|
|
28
|
+
.command("report <path>")
|
|
29
|
+
.description("View a previously generated scan report")
|
|
30
|
+
.option("-f, --format <type>", "Output format: terminal, json", "terminal")
|
|
31
|
+
.action(report_1.reportCommand);
|
|
32
|
+
program
|
|
33
|
+
.command("config")
|
|
34
|
+
.description("Manage Fortly CLI configuration")
|
|
35
|
+
.argument("<action>", "Action: set, get, list, reset")
|
|
36
|
+
.argument("[key]", "Config key (e.g., api-key, default-threshold)")
|
|
37
|
+
.argument("[value]", "Config value")
|
|
38
|
+
.action(config_1.configCommand);
|
|
39
|
+
program.parse();
|
|
40
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;AAAA,yCAAoC;AACpC,0CAA8C;AAC9C,8CAAkD;AAClD,8CAAkD;AAElD,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,6CAA6C,CAAC;KAC1D,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CAAC,gDAAgD,CAAC;KAC7D,MAAM,CAAC,sBAAsB,EAAE,qBAAqB,EAAE,GAAG,CAAC;KAC1D,MAAM,CAAC,wBAAwB,EAAE,uBAAuB,EAAE,OAAO,CAAC;KAClE,MAAM,CAAC,oBAAoB,EAAE,qBAAqB,EAAE,GAAG,CAAC;KACxD,MAAM,CAAC,qBAAqB,EAAE,2CAA2C,EAAE,UAAU,CAAC;KACtF,MAAM,CAAC,oBAAoB,EAAE,8BAA8B,EAAE,kBAAkB,CAAC;KAChF,MAAM,CAAC,2BAA2B,EAAE,oDAAoD,CAAC;KACzF,MAAM,CAAC,SAAS,EAAE,4CAA4C,CAAC;KAC/D,MAAM,CAAC,iBAAiB,EAAE,oDAAoD,CAAC;KAC/E,MAAM,CAAC,SAAS,EAAE,6BAA6B,CAAC;KAChD,MAAM,CAAC,MAAM,EAAE,qDAAqD,CAAC;KACrE,MAAM,CAAC,eAAe,EAAE,gBAAgB,CAAC;KACzC,MAAM,CAAC,kBAAW,CAAC,CAAC;AAEvB,OAAO;KACJ,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,qBAAqB,EAAE,+BAA+B,EAAE,UAAU,CAAC;KAC1E,MAAM,CAAC,sBAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,iCAAiC,CAAC;KAC9C,QAAQ,CAAC,UAAU,EAAE,+BAA+B,CAAC;KACrD,QAAQ,CAAC,OAAO,EAAE,+CAA+C,CAAC;KAClE,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;KACnC,MAAM,CAAC,sBAAa,CAAC,CAAC;AAEzB,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAGA,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,iBA4B/E"}
|
|
@@ -0,0 +1,43 @@
|
|
|
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.configCommand = configCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const store_1 = require("../config/store");
|
|
9
|
+
async function configCommand(action, key, value) {
|
|
10
|
+
const store = new store_1.ConfigStore();
|
|
11
|
+
switch (action) {
|
|
12
|
+
case "set":
|
|
13
|
+
if (!key || !value) {
|
|
14
|
+
console.error("Usage: fortly config set <key> <value>");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
store.set(key, value);
|
|
18
|
+
console.log(chalk_1.default.green(`Set ${key}`));
|
|
19
|
+
break;
|
|
20
|
+
case "get":
|
|
21
|
+
if (!key) {
|
|
22
|
+
console.error("Usage: fortly config get <key>");
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const val = store.get(key);
|
|
26
|
+
console.log(val ?? chalk_1.default.gray("(not set)"));
|
|
27
|
+
break;
|
|
28
|
+
case "list":
|
|
29
|
+
const all = store.getAll();
|
|
30
|
+
for (const [k, v] of Object.entries(all)) {
|
|
31
|
+
console.log(`${chalk_1.default.cyan(k)}: ${k.includes("key") ? "****" + String(v).slice(-4) : v}`);
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
case "reset":
|
|
35
|
+
store.reset();
|
|
36
|
+
console.log(chalk_1.default.green("Configuration reset"));
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
console.error(`Unknown action: ${action}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":";;;;;AAGA,sCA4BC;AA/BD,kDAA0B;AAC1B,2CAA8C;AAEvC,KAAK,UAAU,aAAa,CAAC,MAAc,EAAE,GAAY,EAAE,KAAc;IAC9E,MAAM,KAAK,GAAG,IAAI,mBAAW,EAAE,CAAC;IAEhC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,KAAK;YACR,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;gBAAC,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAAC,CAAC;YACjG,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC;YACvC,MAAM;QACR,KAAK,KAAK;YACR,IAAI,CAAC,GAAG,EAAE,CAAC;gBAAC,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;gBAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAAC,CAAC;YAC/E,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,eAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;YAC5C,MAAM;QACR,KAAK,MAAM;YACT,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YAC3B,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzC,OAAO,CAAC,GAAG,CAAC,GAAG,eAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC3F,CAAC;YACD,MAAM;QACR,KAAK,OAAO;YACV,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;YAChD,MAAM;QACR;YACE,OAAO,CAAC,KAAK,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;YAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":"AAIA,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,iBAe7D"}
|
|
@@ -0,0 +1,25 @@
|
|
|
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.reportCommand = reportCommand;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const terminal_1 = require("../output/terminal");
|
|
10
|
+
async function reportCommand(path, options) {
|
|
11
|
+
if (!(0, fs_1.existsSync)(path)) {
|
|
12
|
+
console.error(chalk_1.default.red(`File not found: ${path}`));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const content = (0, fs_1.readFileSync)(path, "utf-8");
|
|
16
|
+
const report = JSON.parse(content);
|
|
17
|
+
if (options.format === "json") {
|
|
18
|
+
console.log(JSON.stringify(report, null, 2));
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const reporter = new terminal_1.TerminalReporter();
|
|
22
|
+
reporter.printResults(report);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/commands/report.ts"],"names":[],"mappings":";;;;;AAIA,sCAeC;AAnBD,2BAA8C;AAC9C,kDAA0B;AAC1B,iDAAsD;AAE/C,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,OAAY;IAC5D,IAAI,CAAC,IAAA,eAAU,EAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,IAAA,iBAAY,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEnC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,MAAM,QAAQ,GAAG,IAAI,2BAAgB,EAAE,CAAC;QACxC,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAOA,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B7E"}
|
|
@@ -0,0 +1,127 @@
|
|
|
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.scanCommand = scanCommand;
|
|
7
|
+
const ora_1 = __importDefault(require("ora"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const terminal_1 = require("../output/terminal");
|
|
10
|
+
const ci_1 = require("../output/ci");
|
|
11
|
+
const client_1 = require("../api/client");
|
|
12
|
+
const store_1 = require("../config/store");
|
|
13
|
+
async function scanCommand(target, options) {
|
|
14
|
+
const config = new store_1.ConfigStore();
|
|
15
|
+
const isCloud = options.cloud || false;
|
|
16
|
+
const isCi = options.ci || false;
|
|
17
|
+
const reporter = isCi ? new ci_1.CiReporter() : new terminal_1.TerminalReporter();
|
|
18
|
+
// Banner (skip in CI)
|
|
19
|
+
if (!isCi) {
|
|
20
|
+
reporter.banner();
|
|
21
|
+
}
|
|
22
|
+
// Validate target URL
|
|
23
|
+
try {
|
|
24
|
+
new URL(target);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
console.error(chalk_1.default.red(`Invalid URL: ${target}`));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
if (isCloud) {
|
|
31
|
+
// Cloud mode: use API
|
|
32
|
+
const apiKey = options.apiKey || process.env.FT_API_KEY || config.get("api-key");
|
|
33
|
+
if (!apiKey) {
|
|
34
|
+
console.error(chalk_1.default.red("API key required for cloud mode. Use --api-key or set FT_API_KEY"));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
await runCloudScan(target, apiKey, options, reporter);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// Local mode: use embedded engine
|
|
41
|
+
await runLocalScan(target, options, reporter);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function runCloudScan(target, apiKey, options, reporter) {
|
|
45
|
+
const client = new client_1.CloudClient(options.apiUrl || "https://api.fortly.io", apiKey);
|
|
46
|
+
const spinner = (0, ora_1.default)("Creating scan...").start();
|
|
47
|
+
try {
|
|
48
|
+
const scan = await client.createScan(target);
|
|
49
|
+
spinner.text = "Scanning target...";
|
|
50
|
+
const result = await client.waitForCompletion(scan.scanId, 300);
|
|
51
|
+
spinner.succeed("Scan complete");
|
|
52
|
+
reporter.printResults(result);
|
|
53
|
+
handleThreshold(result.score, options.failThreshold);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
spinner.fail(err.message);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function runLocalScan(target, options, reporter) {
|
|
61
|
+
// Dynamic import of the scan engine (from scan-execute lib)
|
|
62
|
+
// In published npm package, this would be bundled
|
|
63
|
+
// For development, we import from the sibling project
|
|
64
|
+
const spinner = (0, ora_1.default)("Initializing scan engine...").start();
|
|
65
|
+
try {
|
|
66
|
+
let runScan;
|
|
67
|
+
try {
|
|
68
|
+
// Dynamic require to avoid TypeScript static analysis on optional dependency
|
|
69
|
+
const engineName = "fortly-scan-engine";
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
71
|
+
const engine = require(engineName);
|
|
72
|
+
runScan = engine.runScan;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
spinner.fail("Scan engine not available. Use --cloud mode or install fortly-scan-engine.");
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
const scanConfig = {
|
|
79
|
+
target,
|
|
80
|
+
maxDepth: parseInt(options.depth) || 3,
|
|
81
|
+
maxRequests: 500,
|
|
82
|
+
timeout: parseInt(options.timeout) || 10000,
|
|
83
|
+
delay: 100,
|
|
84
|
+
threads: parseInt(options.threads) || 5,
|
|
85
|
+
retryAttempts: 2,
|
|
86
|
+
userAgent: "Fortly-CLI/1.0",
|
|
87
|
+
followRedirects: true,
|
|
88
|
+
excludePaths: ["/logout", "/signout"],
|
|
89
|
+
outputDir: options.output || "./fortly-reports",
|
|
90
|
+
outputFormat: options.format === "terminal" ? "both" : options.format,
|
|
91
|
+
verbose: options.verbose || false,
|
|
92
|
+
aiEnabled: options.ai !== false,
|
|
93
|
+
aiMaxCalls: 80,
|
|
94
|
+
};
|
|
95
|
+
spinner.text = `Scanning ${target}...`;
|
|
96
|
+
const report = await runScan(scanConfig);
|
|
97
|
+
spinner.succeed("Scan complete");
|
|
98
|
+
// Print results
|
|
99
|
+
reporter.printResults({
|
|
100
|
+
score: report.score,
|
|
101
|
+
grade: report.grade,
|
|
102
|
+
summary: report.summary,
|
|
103
|
+
vulnerabilities: report.vulnerabilities,
|
|
104
|
+
target: report.target,
|
|
105
|
+
scanDuration: report.scanDuration,
|
|
106
|
+
});
|
|
107
|
+
// Handle output format
|
|
108
|
+
if (options.format !== "terminal") {
|
|
109
|
+
reporter.printReportPaths(options.output);
|
|
110
|
+
}
|
|
111
|
+
handleThreshold(report.score, options.failThreshold);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
spinner.fail(`Scan failed: ${err.message}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function handleThreshold(score, threshold) {
|
|
119
|
+
if (threshold) {
|
|
120
|
+
const t = parseInt(threshold);
|
|
121
|
+
if (score < t) {
|
|
122
|
+
console.error(chalk_1.default.red(`\nScore ${score} is below threshold ${t}. Failing.`));
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/config/store.ts"],"names":[],"mappings":"AAEA,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAO;;IAMpB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAIrC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIjC,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,27 @@
|
|
|
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.ConfigStore = void 0;
|
|
7
|
+
const conf_1 = __importDefault(require("conf"));
|
|
8
|
+
class ConfigStore {
|
|
9
|
+
store;
|
|
10
|
+
constructor() {
|
|
11
|
+
this.store = new conf_1.default({ projectName: "fortly" });
|
|
12
|
+
}
|
|
13
|
+
get(key) {
|
|
14
|
+
return this.store.get(key);
|
|
15
|
+
}
|
|
16
|
+
set(key, value) {
|
|
17
|
+
this.store.set(key, value);
|
|
18
|
+
}
|
|
19
|
+
getAll() {
|
|
20
|
+
return this.store.store;
|
|
21
|
+
}
|
|
22
|
+
reset() {
|
|
23
|
+
this.store.clear();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.ConfigStore = ConfigStore;
|
|
27
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/config/store.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AAExB,MAAa,WAAW;IACd,KAAK,CAAO;IAEpB;QACE,IAAI,CAAC,KAAK,GAAG,IAAI,cAAI,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAuB,CAAC;IACnD,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAa;QAC5B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAtBD,kCAsBC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci.d.ts","sourceRoot":"","sources":["../../src/output/ci.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAU;IACrB,MAAM,IAAI,IAAI;IAId,YAAY,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI;IA6B/B,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAG1C"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CiReporter = void 0;
|
|
4
|
+
class CiReporter {
|
|
5
|
+
banner() {
|
|
6
|
+
// No banner in CI mode
|
|
7
|
+
}
|
|
8
|
+
printResults(result) {
|
|
9
|
+
// Single JSON line to stdout for easy parsing
|
|
10
|
+
const output = {
|
|
11
|
+
score: result.score,
|
|
12
|
+
grade: result.grade,
|
|
13
|
+
target: result.target,
|
|
14
|
+
vulnerabilities: result.summary.totalVulnerabilities,
|
|
15
|
+
critical: result.summary.critical,
|
|
16
|
+
high: result.summary.high,
|
|
17
|
+
medium: result.summary.medium,
|
|
18
|
+
low: result.summary.low,
|
|
19
|
+
info: result.summary.info,
|
|
20
|
+
duration: result.scanDuration,
|
|
21
|
+
};
|
|
22
|
+
console.log(JSON.stringify(output));
|
|
23
|
+
// Also output GitHub Actions annotations if running in GH Actions
|
|
24
|
+
if (process.env.GITHUB_ACTIONS) {
|
|
25
|
+
if (result.summary.critical > 0) {
|
|
26
|
+
console.log(`::error::Fortly found ${result.summary.critical} critical vulnerabilities`);
|
|
27
|
+
}
|
|
28
|
+
if (result.summary.high > 0) {
|
|
29
|
+
console.log(`::warning::Fortly found ${result.summary.high} high severity vulnerabilities`);
|
|
30
|
+
}
|
|
31
|
+
console.log(`::notice::Security Score: ${result.score}/100 (${result.grade})`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
printReportPaths(outputDir) {
|
|
35
|
+
console.log(JSON.stringify({ reportDir: outputDir }));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.CiReporter = CiReporter;
|
|
39
|
+
//# sourceMappingURL=ci.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci.js","sourceRoot":"","sources":["../../src/output/ci.ts"],"names":[],"mappings":";;;AAAA,MAAa,UAAU;IACrB,MAAM;QACJ,uBAAuB;IACzB,CAAC;IAED,YAAY,CAAC,MAAW;QACtB,8CAA8C;QAC9C,MAAM,MAAM,GAAG;YACb,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,oBAAoB;YACpD,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ;YACjC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;YACzB,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM;YAC7B,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG;YACvB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;YACzB,QAAQ,EAAE,MAAM,CAAC,YAAY;SAC9B,CAAC;QAEF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAEpC,kEAAkE;QAClE,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC;YAC/B,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;gBAChC,OAAO,CAAC,GAAG,CAAC,yBAAyB,MAAM,CAAC,OAAO,CAAC,QAAQ,2BAA2B,CAAC,CAAC;YAC3F,CAAC;YACD,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAC5B,OAAO,CAAC,GAAG,CAAC,2BAA2B,MAAM,CAAC,OAAO,CAAC,IAAI,gCAAgC,CAAC,CAAC;YAC9F,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,6BAA6B,MAAM,CAAC,KAAK,SAAS,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,gBAAgB,CAAC,SAAiB;QAChC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IACxD,CAAC;CACF;AArCD,gCAqCC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface SarifReport {
|
|
2
|
+
$schema: string;
|
|
3
|
+
version: string;
|
|
4
|
+
runs: Array<{
|
|
5
|
+
tool: {
|
|
6
|
+
driver: {
|
|
7
|
+
name: string;
|
|
8
|
+
version: string;
|
|
9
|
+
informationUri: string;
|
|
10
|
+
rules: any[];
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
results: any[];
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
export declare class SarifExporter {
|
|
17
|
+
static generate(result: any): SarifReport;
|
|
18
|
+
static write(result: any, outputPath: string): void;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=sarif.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../../src/output/sarif.ts"],"names":[],"mappings":"AAAA,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,KAAK,CAAC;QACV,IAAI,EAAE;YAAE,MAAM,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,CAAC;gBAAC,cAAc,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,GAAG,EAAE,CAAA;aAAE,CAAA;SAAE,CAAC;QAC1F,OAAO,EAAE,GAAG,EAAE,CAAC;KAChB,CAAC,CAAC;CACJ;AAED,qBAAa,aAAa;IACxB,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,WAAW;IA6CzC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;CAMpD"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SarifExporter = void 0;
|
|
4
|
+
class SarifExporter {
|
|
5
|
+
static generate(result) {
|
|
6
|
+
const rules = (result.vulnerabilities || []).map((v, i) => ({
|
|
7
|
+
id: `VH-${v.type}`,
|
|
8
|
+
name: v.type,
|
|
9
|
+
shortDescription: { text: v.title },
|
|
10
|
+
fullDescription: { text: v.description || v.title },
|
|
11
|
+
defaultConfiguration: {
|
|
12
|
+
level: v.severity === "critical" || v.severity === "high" ? "error" :
|
|
13
|
+
v.severity === "medium" ? "warning" : "note",
|
|
14
|
+
},
|
|
15
|
+
helpUri: `https://docs.fortly.io/vulns/${v.type}`,
|
|
16
|
+
}));
|
|
17
|
+
// Deduplicate rules by id
|
|
18
|
+
const uniqueRules = [...new Map(rules.map((r) => [r.id, r])).values()];
|
|
19
|
+
const results = (result.vulnerabilities || []).map((v) => ({
|
|
20
|
+
ruleId: `VH-${v.type}`,
|
|
21
|
+
level: v.severity === "critical" || v.severity === "high" ? "error" :
|
|
22
|
+
v.severity === "medium" ? "warning" : "note",
|
|
23
|
+
message: { text: `${v.title}: ${v.description || ""}`.trim() },
|
|
24
|
+
locations: [{
|
|
25
|
+
physicalLocation: {
|
|
26
|
+
artifactLocation: { uri: v.url || result.target },
|
|
27
|
+
},
|
|
28
|
+
}],
|
|
29
|
+
}));
|
|
30
|
+
return {
|
|
31
|
+
$schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
|
32
|
+
version: "2.1.0",
|
|
33
|
+
runs: [{
|
|
34
|
+
tool: {
|
|
35
|
+
driver: {
|
|
36
|
+
name: "Fortly",
|
|
37
|
+
version: "1.0.0",
|
|
38
|
+
informationUri: "https://fortly.io",
|
|
39
|
+
rules: uniqueRules,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
results,
|
|
43
|
+
}],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
static write(result, outputPath) {
|
|
47
|
+
const { writeFileSync, mkdirSync } = require("fs");
|
|
48
|
+
const { dirname } = require("path");
|
|
49
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
50
|
+
writeFileSync(outputPath, JSON.stringify(SarifExporter.generate(result), null, 2));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.SarifExporter = SarifExporter;
|
|
54
|
+
//# sourceMappingURL=sarif.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../src/output/sarif.ts"],"names":[],"mappings":";;;AASA,MAAa,aAAa;IACxB,MAAM,CAAC,QAAQ,CAAC,MAAW;QACzB,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC;YACvE,EAAE,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE;YAClB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,gBAAgB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE;YACnC,eAAe,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,KAAK,EAAE;YACnD,oBAAoB,EAAE;gBACpB,KAAK,EAAE,CAAC,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;oBAC9D,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;aACpD;YACD,OAAO,EAAE,gCAAgC,CAAC,CAAC,IAAI,EAAE;SAClD,CAAC,CAAC,CAAC;QAEJ,0BAA0B;QAC1B,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QAE5E,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC9D,MAAM,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE;YACtB,KAAK,EAAE,CAAC,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBAC9D,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;YACnD,OAAO,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;YAC9D,SAAS,EAAE,CAAC;oBACV,gBAAgB,EAAE;wBAChB,gBAAgB,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE;qBAClD;iBACF,CAAC;SACH,CAAC,CAAC,CAAC;QAEJ,OAAO;YACL,OAAO,EAAE,gGAAgG;YACzG,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,CAAC;oBACL,IAAI,EAAE;wBACJ,MAAM,EAAE;4BACN,IAAI,EAAE,QAAQ;4BACd,OAAO,EAAE,OAAO;4BAChB,cAAc,EAAE,mBAAmB;4BACnC,KAAK,EAAE,WAAW;yBACnB;qBACF;oBACD,OAAO;iBACR,CAAC;SACH,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,MAAW,EAAE,UAAkB;QAC1C,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QACpC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACrF,CAAC;CACF;AApDD,sCAoDC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface ScanResultData {
|
|
2
|
+
score: number;
|
|
3
|
+
grade: string;
|
|
4
|
+
target: string;
|
|
5
|
+
scanDuration?: number;
|
|
6
|
+
summary: {
|
|
7
|
+
totalVulnerabilities: number;
|
|
8
|
+
critical: number;
|
|
9
|
+
high: number;
|
|
10
|
+
medium: number;
|
|
11
|
+
low: number;
|
|
12
|
+
info: number;
|
|
13
|
+
};
|
|
14
|
+
vulnerabilities: Array<{
|
|
15
|
+
severity: string;
|
|
16
|
+
title: string;
|
|
17
|
+
type: string;
|
|
18
|
+
url: string;
|
|
19
|
+
cwe?: string;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export declare class TerminalReporter {
|
|
23
|
+
banner(): void;
|
|
24
|
+
printResults(result: ScanResultData): void;
|
|
25
|
+
printReportPaths(outputDir: string): void;
|
|
26
|
+
private printScoreBar;
|
|
27
|
+
private getScoreColor;
|
|
28
|
+
private severityBadge;
|
|
29
|
+
private colorCount;
|
|
30
|
+
private formatDuration;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../src/output/terminal.ts"],"names":[],"mappings":"AAGA,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE;QACP,oBAAoB,EAAE,MAAM,CAAC;QAC7B,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,eAAe,EAAE,KAAK,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ;AAED,qBAAa,gBAAgB;IAC3B,MAAM,IAAI,IAAI;IAYd,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IA6E1C,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAMzC,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,cAAc;CAKvB"}
|
|
@@ -0,0 +1,130 @@
|
|
|
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.TerminalReporter = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
9
|
+
class TerminalReporter {
|
|
10
|
+
banner() {
|
|
11
|
+
console.log("");
|
|
12
|
+
console.log(chalk_1.default.bold.red(" ██╗ ██╗██╗ ██╗██╗ ███╗ ██╗"));
|
|
13
|
+
console.log(chalk_1.default.bold.red(" ██║ ██║██║ ██║██║ ████╗ ██║"));
|
|
14
|
+
console.log(chalk_1.default.bold.red(" ██║ ██║██║ ██║██║ ██╔██╗ ██║"));
|
|
15
|
+
console.log(chalk_1.default.bold.red(" ╚██╗ ██╔╝██║ ██║██║ ██║╚██╗██║"));
|
|
16
|
+
console.log(chalk_1.default.bold.red(" ╚████╔╝ ╚██████╔╝███████╗██║ ╚████║"));
|
|
17
|
+
console.log(chalk_1.default.bold.red(" ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝"));
|
|
18
|
+
console.log(chalk_1.default.gray(" Fortly CLI — Automated Web Security Scanner"));
|
|
19
|
+
console.log(chalk_1.default.gray(" Use only on applications you have authorization to test.\n"));
|
|
20
|
+
}
|
|
21
|
+
printResults(result) {
|
|
22
|
+
console.log("");
|
|
23
|
+
// Score section
|
|
24
|
+
const scoreColor = this.getScoreColor(result.score);
|
|
25
|
+
console.log(chalk_1.default.bold(" Target: ") + chalk_1.default.cyan(result.target));
|
|
26
|
+
if (result.scanDuration) {
|
|
27
|
+
console.log(chalk_1.default.bold(" Duration: ") + chalk_1.default.gray(this.formatDuration(result.scanDuration)));
|
|
28
|
+
}
|
|
29
|
+
console.log("");
|
|
30
|
+
console.log(chalk_1.default.bold(" Security Score: ") + scoreColor(` ${result.score}/100 `) + chalk_1.default.bold(` Grade: ${result.grade}`));
|
|
31
|
+
console.log("");
|
|
32
|
+
// Progress bar
|
|
33
|
+
this.printScoreBar(result.score);
|
|
34
|
+
console.log("");
|
|
35
|
+
// Summary table
|
|
36
|
+
const summaryTable = new cli_table3_1.default({
|
|
37
|
+
head: [
|
|
38
|
+
chalk_1.default.white("Severity"),
|
|
39
|
+
chalk_1.default.white("Count"),
|
|
40
|
+
],
|
|
41
|
+
colWidths: [20, 10],
|
|
42
|
+
style: { head: [], border: ["gray"] },
|
|
43
|
+
});
|
|
44
|
+
summaryTable.push([chalk_1.default.red("Critical"), this.colorCount(result.summary.critical, "critical")], [chalk_1.default.hex("#ff6b35")("High"), this.colorCount(result.summary.high, "high")], [chalk_1.default.yellow("Medium"), this.colorCount(result.summary.medium, "medium")], [chalk_1.default.green("Low"), this.colorCount(result.summary.low, "low")], [chalk_1.default.gray("Info"), String(result.summary.info)], [chalk_1.default.bold("Total"), chalk_1.default.bold(String(result.summary.totalVulnerabilities))]);
|
|
45
|
+
console.log(summaryTable.toString());
|
|
46
|
+
console.log("");
|
|
47
|
+
// Top vulnerabilities (max 10)
|
|
48
|
+
if (result.vulnerabilities.length > 0) {
|
|
49
|
+
console.log(chalk_1.default.bold(" Top Findings:"));
|
|
50
|
+
console.log("");
|
|
51
|
+
const vulnTable = new cli_table3_1.default({
|
|
52
|
+
head: [
|
|
53
|
+
chalk_1.default.white("Sev"),
|
|
54
|
+
chalk_1.default.white("Title"),
|
|
55
|
+
chalk_1.default.white("Type"),
|
|
56
|
+
chalk_1.default.white("URL"),
|
|
57
|
+
],
|
|
58
|
+
colWidths: [12, 30, 22, 40],
|
|
59
|
+
style: { head: [], border: ["gray"] },
|
|
60
|
+
wordWrap: true,
|
|
61
|
+
});
|
|
62
|
+
for (const vuln of result.vulnerabilities.slice(0, 10)) {
|
|
63
|
+
vulnTable.push([
|
|
64
|
+
this.severityBadge(vuln.severity),
|
|
65
|
+
vuln.title,
|
|
66
|
+
vuln.type,
|
|
67
|
+
vuln.url.length > 38 ? vuln.url.substring(0, 35) + "..." : vuln.url,
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
70
|
+
console.log(vulnTable.toString());
|
|
71
|
+
if (result.vulnerabilities.length > 10) {
|
|
72
|
+
console.log(chalk_1.default.gray(` ... and ${result.vulnerabilities.length - 10} more vulnerabilities`));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.log(chalk_1.default.green(" No vulnerabilities found. The target appears well-secured."));
|
|
77
|
+
}
|
|
78
|
+
console.log("");
|
|
79
|
+
}
|
|
80
|
+
printReportPaths(outputDir) {
|
|
81
|
+
console.log(chalk_1.default.bold(" Reports saved to:"));
|
|
82
|
+
console.log(chalk_1.default.cyan(` ${outputDir}/`));
|
|
83
|
+
console.log("");
|
|
84
|
+
}
|
|
85
|
+
printScoreBar(score) {
|
|
86
|
+
const width = 40;
|
|
87
|
+
const filled = Math.round((score / 100) * width);
|
|
88
|
+
const empty = width - filled;
|
|
89
|
+
const color = this.getScoreColor(score);
|
|
90
|
+
const bar = color("█".repeat(filled)) + chalk_1.default.gray("░".repeat(empty));
|
|
91
|
+
console.log(` ${bar} ${score}%`);
|
|
92
|
+
}
|
|
93
|
+
getScoreColor(score) {
|
|
94
|
+
if (score >= 90)
|
|
95
|
+
return chalk_1.default.bgGreen.black;
|
|
96
|
+
if (score >= 70)
|
|
97
|
+
return chalk_1.default.bgYellow.black;
|
|
98
|
+
if (score >= 50)
|
|
99
|
+
return chalk_1.default.bgHex("#ff6b35").black;
|
|
100
|
+
return chalk_1.default.bgRed.white;
|
|
101
|
+
}
|
|
102
|
+
severityBadge(severity) {
|
|
103
|
+
switch (severity.toLowerCase()) {
|
|
104
|
+
case "critical": return chalk_1.default.bgRed.white.bold(" CRIT ");
|
|
105
|
+
case "high": return chalk_1.default.bgHex("#ff6b35").white.bold(" HIGH ");
|
|
106
|
+
case "medium": return chalk_1.default.bgYellow.black(" MED ");
|
|
107
|
+
case "low": return chalk_1.default.bgGreen.black(" LOW ");
|
|
108
|
+
default: return chalk_1.default.bgGray.white(" INFO ");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
colorCount(count, severity) {
|
|
112
|
+
if (count === 0)
|
|
113
|
+
return chalk_1.default.green("0");
|
|
114
|
+
switch (severity) {
|
|
115
|
+
case "critical": return chalk_1.default.red.bold(String(count));
|
|
116
|
+
case "high": return chalk_1.default.hex("#ff6b35").bold(String(count));
|
|
117
|
+
case "medium": return chalk_1.default.yellow(String(count));
|
|
118
|
+
default: return String(count);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
formatDuration(ms) {
|
|
122
|
+
if (ms < 1000)
|
|
123
|
+
return `${ms}ms`;
|
|
124
|
+
if (ms < 60000)
|
|
125
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
126
|
+
return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
exports.TerminalReporter = TerminalReporter;
|
|
130
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/output/terminal.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fortly-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Fortly CLI — Automated web security scanner with AI-powered vulnerability detection",
|
|
5
|
+
"bin": {
|
|
6
|
+
"fortly": "./bin/fortly.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "dist/cli.js",
|
|
9
|
+
"files": ["bin/", "dist/", "README.md", "LICENSE"],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "jest --coverage",
|
|
13
|
+
"start": "ts-node src/cli.ts"
|
|
14
|
+
},
|
|
15
|
+
"keywords": ["security", "scanner", "vulnerability", "dast", "owasp", "pentest", "cli"],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"commander": "^12.0.0",
|
|
18
|
+
"ora": "^5.4.1",
|
|
19
|
+
"chalk": "^4.1.2",
|
|
20
|
+
"cli-table3": "^0.6.0",
|
|
21
|
+
"conf": "^10.2.0",
|
|
22
|
+
"axios": "^1.6.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.0.0",
|
|
26
|
+
"@types/jest": "^29.0.0",
|
|
27
|
+
"jest": "^29.0.0",
|
|
28
|
+
"ts-jest": "^29.0.0",
|
|
29
|
+
"ts-node": "^10.9.2",
|
|
30
|
+
"typescript": "^5.0.0"
|
|
31
|
+
},
|
|
32
|
+
"engines": { "node": ">=18.0.0" }
|
|
33
|
+
}
|