@authtrack/secura 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -1,157 +1,229 @@
1
- # @authtrack/secura
2
-
3
- **AuthTrack static analysis (SAST) from the command line.**
4
-
5
- `secura` points five independent engines — [Semgrep], [Bearer], [OSV-Scanner],
6
- [Gitleaks] and [CodeQL] — at a repository, merges and deduplicates their
7
- findings, retrieves similar known-vulnerable patterns, then has an LLM confirm
8
- or rule out each finding and write a patch for the confirmed ones. It streams
9
- the whole run live in your terminal and prints a report at the end.
10
-
11
- The scanning runs on the **AuthTrack backend**; this CLI is a thin, zero-dependency
12
- client that talks to it. You don't need Python, and you don't need the scanners
13
- installed locally.
14
-
15
- [Semgrep]: https://semgrep.dev
16
- [Bearer]: https://www.bearer.com
17
- [OSV-Scanner]: https://google.github.io/osv-scanner/
18
- [Gitleaks]: https://github.com/gitleaks/gitleaks
19
- [CodeQL]: https://codeql.github.com
20
-
21
- ---
22
-
23
- ## Install
24
-
25
- ```bash
26
- npm install -g @authtrack/secura
27
- ```
28
-
29
- Or run it once, without installing:
30
-
31
- ```bash
32
- npx @authtrack/secura scan .
33
- ```
34
-
35
- Requires **Node ≥ 18**. No build step, no native modules.
36
-
37
- ## Quick start
38
-
39
- ```bash
40
- # Scan the current directory
41
- secura scan .
42
-
43
- # Scan a public GitHub repo (the backend clones it)
44
- secura scan owner/repo
45
- secura scan https://github.com/owner/repo
46
-
47
- # Fast pass — skip CodeQL (its database build dominates the run)
48
- secura scan . --fast
49
-
50
- # CI gate: exit non-zero if anything HIGH or worse is confirmed
51
- secura scan . --fail-on high
52
-
53
- # Save the full machine-readable report
54
- secura scan . --json report.json
55
- ```
56
-
57
- ## Targets
58
-
59
- | Target | What happens |
60
- | --- | --- |
61
- | `.` or `./path/to/repo` | A local folder. Sent to the backend to scan (see [Backend](#backend)). |
62
- | `owner/repo` | Public GitHub shorthand. The backend shallow-clones it, scans, then deletes it. |
63
- | `https://github.com/owner/repo`, `git@…`, `ssh://…` | Any public Git URL. Cloned by the backend. |
64
-
65
- For a local path, how the code reaches the backend depends on where the backend runs:
66
-
67
- - **Local backend** (the default, `http://localhost:8000`): the absolute path is
68
- sent as-is and scanned in place — nothing is copied.
69
- - **Remote backend** (a `--api` that isn't localhost), or **`--upload`**: the
70
- directory is packaged into a `tar.gz` (excluding `node_modules`, `.git`, build
71
- output, lockfiles, minified assets, …) and uploaded. Requires `tar` on your
72
- PATH (bundled with Windows 10 1803+, macOS and Linux). If `tar` is missing,
73
- scan a Git URL instead.
74
-
75
- ## Options
76
-
77
- ```
78
- -f, --fast Skip CodeQL (its DB build dominates the run).
79
- -l, --language <lang> CodeQL language (javascript, python, java, go, ruby,
80
- csharp, cpp). Auto-detected when omitted.
81
- -t, --max-triage <n> Cap findings sent to LLM triage (default 25).
82
- --fail-on <sev> Exit 1 if a confirmed finding is >= this severity
83
- (critical|high|medium|low). For CI gates.
84
- -o, --json <path> Write the full report as JSON to <path>.
85
- --max-findings <n> How many confirmed findings to print (default 20).
86
- --show-fixes Print the generated fix diffs in full.
87
- -q, --quiet Suppress the live view; print only the report.
88
- --api <url> Backend base URL (default $SECURA_API or
89
- http://localhost:8000/api/v1).
90
- --token <token> Bearer token, if the backend requires one
91
- (default $SECURA_TOKEN).
92
- --upload Package and upload the local dir even for a local
93
- backend (automatic for a remote --api).
94
- ```
95
-
96
- ### Exit codes
97
-
98
- | Code | Meaning |
99
- | --- | --- |
100
- | `0` | Completed; no `--fail-on` breach. |
101
- | `1` | `--fail-on` gate breached (a confirmed finding at or above the threshold). |
102
- | `2` | Bad target, or the scan failed. |
103
- | `130` | Interrupted (Ctrl-C). |
104
-
105
- ## Backend
106
-
107
- `secura` needs a reachable AuthTrack backend. Point it at one with `--api` or the
108
- `SECURA_API` environment variable (matching the web app's `NEXT_PUBLIC_API_URL`):
109
-
110
- ```bash
111
- export SECURA_API="https://scans.example.com/api/v1"
112
- secura scan owner/repo
113
- ```
114
-
115
- The default is `http://localhost:8000/api/v1`. The scan endpoints are unauthenticated;
116
- `--token` / `SECURA_TOKEN` is sent as a bearer header only if provided, so it keeps
117
- working if auth is added later.
118
-
119
- ## CI example
120
-
121
- ```yaml
122
- # GitHub Actions — fail the build on a confirmed high+ finding
123
- - name: SAST
124
- run: npx @authtrack/secura scan . --fast --fail-on high --json sast.json
125
- env:
126
- SECURA_API: ${{ secrets.SECURA_API }}
127
- ```
128
-
129
- ---
130
-
131
- ## Publishing (maintainers)
132
-
133
- This package publishes from the `cli/` directory of the AuthTrack repo. It is
134
- plain ESM with no build step, so publishing is just:
135
-
136
- ```bash
137
- cd cli
138
- npm login # once per machine
139
- npm version patch # 0.1.0 -> 0.1.1 (bumps package.json + git tag)
140
- npm publish --access public # scoped packages need --access public on first publish
141
- ```
142
-
143
- Notes:
144
-
145
- - **Scope.** The name `@authtrack/secura` requires the `@authtrack` org to exist
146
- on npm and your account to be a member. Create it at
147
- <https://www.npmjs.com/org/create>, or rename the package to an unscoped name
148
- you own (e.g. `authtrack-secura`) in `package.json` — the `bin` stays `secura`
149
- either way, so `secura scan …` is unchanged for users.
150
- - **What ships.** Only `bin/`, `src/` and `README.md` (the `files` allowlist).
151
- Verify with `npm pack --dry-run` before publishing.
152
- - **Smoke test the tarball.** `npm pack` then
153
- `npm i -g ./authtrack-secura-<version>.tgz` and run `secura --help`.
154
-
155
- ## License
156
-
157
- MIT
1
+ # @authtrack/secura
2
+
3
+ **AuthTrack static analysis (SAST) from the command line.**
4
+
5
+ `secura` points five independent engines — [Semgrep], [Bearer], [OSV-Scanner],
6
+ [Gitleaks] and [CodeQL] — at a repository, merges and deduplicates their
7
+ findings, retrieves similar known-vulnerable patterns, then has an LLM confirm
8
+ or rule out each finding and write a patch for the confirmed ones. It streams
9
+ the whole run live in your terminal and prints a report at the end.
10
+
11
+ The scanning runs on the **AuthTrack backend**; this CLI is a thin, zero-dependency
12
+ client that talks to it. You don't need Python, and you don't need the scanners
13
+ installed locally.
14
+
15
+ [Semgrep]: https://semgrep.dev
16
+ [Bearer]: https://www.bearer.com
17
+ [OSV-Scanner]: https://google.github.io/osv-scanner/
18
+ [Gitleaks]: https://github.com/gitleaks/gitleaks
19
+ [CodeQL]: https://codeql.github.com
20
+
21
+ ---
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ npm install -g @authtrack/secura
27
+ ```
28
+
29
+ Or run it once, without installing:
30
+
31
+ ```bash
32
+ npx @authtrack/secura scan .
33
+ ```
34
+
35
+ Requires **Node ≥ 18**. No build step, no native modules.
36
+
37
+ ## Quick start
38
+
39
+ ```bash
40
+ # Scan the current directory
41
+ secura scan .
42
+
43
+ # Scan a public GitHub repo (the backend clones it)
44
+ secura scan owner/repo
45
+ secura scan https://github.com/owner/repo
46
+
47
+ # Fast pass — skip CodeQL (its database build dominates the run)
48
+ secura scan . --fast
49
+
50
+ # CI gate: exit non-zero if anything HIGH or worse is confirmed
51
+ secura scan . --fail-on high
52
+
53
+ # Save the full machine-readable report
54
+ secura scan . --json report.json
55
+ ```
56
+
57
+ ## Targets
58
+
59
+ | Target | What happens |
60
+ | --- | --- |
61
+ | `.` or `./path/to/repo` | A local folder. Sent to the backend to scan (see [Backend](#backend)). |
62
+ | `owner/repo` | Public GitHub shorthand. The backend shallow-clones it, scans, then deletes it. |
63
+ | `https://github.com/owner/repo`, `git@…`, `ssh://…` | Any public Git URL. Cloned by the backend. |
64
+
65
+ For a local path, how the code reaches the backend depends on where the backend runs:
66
+
67
+ - **Local backend** (the default, `http://localhost:8000`): the absolute path is
68
+ sent as-is and scanned in place — nothing is copied.
69
+ - **Remote backend** (a `--api` that isn't localhost), or **`--upload`**: the
70
+ directory is packaged into a `tar.gz` (excluding `node_modules`, `.git`, build
71
+ output, lockfiles, minified assets, …) and uploaded. Requires `tar` on your
72
+ PATH (bundled with Windows 10 1803+, macOS and Linux). If `tar` is missing,
73
+ scan a Git URL instead.
74
+
75
+ ## Options
76
+
77
+ ```
78
+ -f, --fast Skip CodeQL (its DB build dominates the run).
79
+ -l, --language <lang> CodeQL language (javascript, python, java, go, ruby,
80
+ csharp, cpp). Auto-detected when omitted.
81
+ -t, --max-triage <n> Cap findings sent to LLM triage (default 25).
82
+ --fail-on <sev> Exit 1 if a confirmed finding is >= this severity
83
+ (critical|high|medium|low). For CI gates.
84
+ -o, --json <path> Write the full report as JSON to <path>.
85
+ --max-findings <n> How many confirmed findings to print (default 20).
86
+ --show-fixes Print the generated fix diffs in full.
87
+ -q, --quiet Suppress the live view; print only the report.
88
+ --api <url> Backend base URL (default $SECURA_API or
89
+ http://localhost:8000/api/v1).
90
+ --token <token> Bearer token, if the backend requires one
91
+ (default $SECURA_TOKEN).
92
+ --upload Package and upload the local dir even for a local
93
+ backend (automatic for a remote --api).
94
+ ```
95
+
96
+ ### Exit codes
97
+
98
+ | Code | Meaning |
99
+ | --- | --- |
100
+ | `0` | Completed; no `--fail-on` breach. |
101
+ | `1` | `--fail-on` gate breached (a confirmed finding at or above the threshold). |
102
+ | `2` | Bad target, or the scan failed. |
103
+ | `130` | Interrupted (Ctrl-C). |
104
+
105
+ ## Backend
106
+
107
+ `secura` needs a reachable AuthTrack backend. Point it at one with `--api` or the
108
+ `SECURA_API` environment variable (matching the web app's `NEXT_PUBLIC_API_URL`):
109
+
110
+ ```bash
111
+ export SECURA_API="https://scans.example.com/api/v1"
112
+ secura scan owner/repo
113
+ ```
114
+
115
+ The default is `http://localhost:8000/api/v1`. The scan endpoints are unauthenticated;
116
+ `--token` / `SECURA_TOKEN` is sent as a bearer header only if provided, so it keeps
117
+ working if auth is added later.
118
+
119
+ ## CI example
120
+
121
+ ```yaml
122
+ # GitHub Actions — fail the build on a confirmed high+ finding
123
+ - name: SAST
124
+ run: npx @authtrack/secura scan . --fast --fail-on high --json sast.json
125
+ env:
126
+ SECURA_API: ${{ secrets.SECURA_API }}
127
+ ```
128
+
129
+ ---
130
+
131
+ ## MCP Server (`secura-mcp`)
132
+
133
+ After `npm install -g @authtrack/secura`, a second command `secura-mcp` is also installed.
134
+ **You never run it yourself** — your AI editor runs it automatically as a background subprocess
135
+ and exposes its tools to the AI assistant.
136
+
137
+ ### Tools exposed
138
+
139
+ | Tool | What it does |
140
+ |------|-------------|
141
+ | `run_static_scan` | SAST — Semgrep, Bearer, OSV-Scanner, Gitleaks, CodeQL + AI triage |
142
+ | `run_recon` | Surface mapping — crawl & classify endpoints |
143
+ | `run_header_audit` | OWASP security-header & cookie audit |
144
+
145
+ ### Claude Code setup
146
+
147
+ Create or edit `~/.claude/claude_desktop_config.json` (or the path shown in Claude's settings):
148
+
149
+ ```json
150
+ {
151
+ "mcpServers": {
152
+ "secura": {
153
+ "command": "secura-mcp",
154
+ "env": {
155
+ "SECURA_API": "http://localhost:8000/api/v1",
156
+ "SECURA_TOKEN": "<your-jwt-if-needed>"
157
+ }
158
+ }
159
+ }
160
+ }
161
+ ```
162
+
163
+ ### Cursor setup
164
+
165
+ Open **Cursor → Settings → Features → MCP** and add:
166
+
167
+ ```json
168
+ {
169
+ "secura": {
170
+ "command": "secura-mcp",
171
+ "env": {
172
+ "SECURA_API": "http://localhost:8000/api/v1",
173
+ "SECURA_TOKEN": "<your-jwt-if-needed>"
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### Usage (after setup)
180
+
181
+ Just talk to your AI assistant as normal — it will automatically call the tools when relevant:
182
+
183
+ ```
184
+ "Check this repo for vulnerabilities"
185
+ → AI calls run_static_scan({ target_path: "." })
186
+
187
+ "Map the attack surface of https://example.com"
188
+ → AI calls run_recon({ url: "https://example.com" })
189
+
190
+ "Audit the security headers on my staging server"
191
+ → AI calls run_header_audit({ url: "https://staging.example.com" })
192
+ ```
193
+
194
+ ### Environment variables
195
+
196
+ | Variable | Default | Description |
197
+ |----------|---------|-------------|
198
+ | `SECURA_API` | `https://securaai-major-project.onrender.com/api/v1` | Backend base URL |
199
+ | `SECURA_TOKEN` | _(empty)_ | Bearer token for authenticated backends |
200
+
201
+ ---
202
+
203
+
204
+
205
+ This package publishes from the `cli/` directory of the AuthTrack repo. It is
206
+ plain ESM with no build step, so publishing is just:
207
+
208
+ ```bash
209
+ cd cli
210
+ npm login # once per machine
211
+ npm version patch # 0.1.0 -> 0.1.1 (bumps package.json + git tag)
212
+ npm publish --access public # scoped packages need --access public on first publish
213
+ ```
214
+
215
+ Notes:
216
+
217
+ - **Scope.** The name `@authtrack/secura` requires the `@SecuraAI` org to exist
218
+ on npm and your account to be a member. Create it at
219
+ <https://www.npmjs.com/org/create>, or rename the package to an unscoped name
220
+ you own (e.g. `securai-secura`) in `package.json` — the `bin` stays `secura`
221
+ either way, so `secura scan …` is unchanged for users.
222
+ - **What ships.** Only `bin/`, `src/` and `README.md` (the `files` allowlist).
223
+ Verify with `npm pack --dry-run` before publishing.
224
+ - **Smoke test the tarball.** `npm pack` then
225
+ `npm i -g ./securai-secura-<version>.tgz` and run `secura --help`.
226
+
227
+ ## License
228
+
229
+ MIT
@@ -0,0 +1,412 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * secura-mcp — SecuraAI MCP server (stdio transport).
4
+ *
5
+ * Exposes four security-scanning tools to any MCP-capable AI editor
6
+ * (Claude Code, Cursor, Continue, etc.). The editor spawns this process
7
+ * automatically; users never run it themselves.
8
+ *
9
+ * Tools exposed:
10
+ * run_static_scan — SAST (Semgrep, Bearer, OSV-Scanner, Gitleaks, CodeQL)
11
+ * run_recon — Surface mapping / endpoint discovery
12
+ * run_header_audit — Security-header & cookie audit
13
+ * run_injection_scan — SQL / command / XSS injection testing
14
+ *
15
+ * Configuration (environment variables, all optional):
16
+ * SECURA_API — Backend base URL (default: https://securaai-major-project.onrender.com/api/v1)
17
+ * SECURA_TOKEN — Bearer token for authenticated backends
18
+ *
19
+ * Usage in Claude Code / Cursor mcp config:
20
+ * {
21
+ * "mcpServers": {
22
+ * "secura": {
23
+ * "command": "secura-mcp",
24
+ * "env": {
25
+ * "SECURA_API": "http://localhost:8000/api/v1",
26
+ * "SECURA_TOKEN": "<your-jwt>"
27
+ * }
28
+ * }
29
+ * }
30
+ * }
31
+ */
32
+
33
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
34
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
35
+ import {
36
+ CallToolRequestSchema,
37
+ ListToolsRequestSchema,
38
+ } from "@modelcontextprotocol/sdk/types.js";
39
+
40
+ // ── Config ───────────────────────────────────────────────────────────────────
41
+
42
+ const DEFAULT_API = "https://securaai-major-project.onrender.com/api/v1";
43
+
44
+ function resolveConfig() {
45
+ const api = (process.env.SECURA_API || DEFAULT_API).replace(/\/+$/, "");
46
+ const token = process.env.SECURA_TOKEN || "";
47
+ return { api, token };
48
+ }
49
+
50
+ function authHeaders(token, extra = {}) {
51
+ const h = { ...extra };
52
+ if (token) h.Authorization = `Bearer ${token}`;
53
+ return h;
54
+ }
55
+
56
+ // ── SSE streaming helpers ────────────────────────────────────────────────────
57
+
58
+ async function* readSse(res) {
59
+ const decoder = new TextDecoder();
60
+ const reader = res.body.getReader();
61
+ let buffer = "";
62
+
63
+ const emit = function* (frame) {
64
+ for (const line of frame.split("\n")) {
65
+ if (!line.startsWith("data:")) continue;
66
+ const json = line.slice(line.indexOf(":") + 1).trim();
67
+ if (!json) continue;
68
+ try { yield JSON.parse(json); } catch { /* skip malformed */ }
69
+ }
70
+ };
71
+
72
+ while (true) {
73
+ const { done, value } = await reader.read();
74
+ if (done) break;
75
+ buffer += decoder.decode(value, { stream: true });
76
+ const frames = buffer.split("\n\n");
77
+ buffer = frames.pop() || "";
78
+ for (const f of frames) yield* emit(f);
79
+ }
80
+ const tail = buffer.trim();
81
+ if (tail) yield* emit(tail);
82
+ }
83
+
84
+ /**
85
+ * Drain an SSE stream and collect the accumulated graph state.
86
+ * Returns { finalState, scanId, error }.
87
+ */
88
+ async function drainStream(url, token, headers, body) {
89
+ let res;
90
+ try {
91
+ res = await fetch(url, {
92
+ method: "POST",
93
+ headers: authHeaders(token, headers),
94
+ body,
95
+ });
96
+ } catch (err) {
97
+ throw new Error(`Cannot reach SecuraAI backend at ${url}: ${err.message}`);
98
+ }
99
+
100
+ if (!res.ok) {
101
+ const detail = await res.text().catch(() => "");
102
+ throw new Error(`Backend error ${res.status}: ${detail.slice(0, 400)}`);
103
+ }
104
+
105
+ const accumulated = {};
106
+ let scanId = null;
107
+ let lastError = null;
108
+
109
+ for await (const evt of readSse(res)) {
110
+ if (evt.event === "node_update" && evt.state) {
111
+ Object.assign(accumulated, evt.state);
112
+ }
113
+ if (evt.event === "complete") {
114
+ scanId = evt.scan_id ?? null;
115
+ }
116
+ if (evt.event === "error") {
117
+ lastError = evt.message;
118
+ }
119
+ }
120
+
121
+ return { finalState: accumulated, scanId, error: lastError };
122
+ }
123
+
124
+ // ── Tool implementations ─────────────────────────────────────────────────────
125
+
126
+ /** SAST — static analysis scan. */
127
+ async function runStaticScan(args) {
128
+ const { api, token } = resolveConfig();
129
+ const body = JSON.stringify({
130
+ target_path: args.target_path,
131
+ include_codeql: args.include_codeql ?? true,
132
+ codeql_language: args.codeql_language ?? "",
133
+ max_triage: args.max_triage ?? 25,
134
+ });
135
+
136
+ const { finalState, scanId, error } = await drainStream(
137
+ `${api}/scan/stream/static`, token,
138
+ { "Content-Type": "application/json" }, body,
139
+ );
140
+
141
+ if (error) throw new Error(error);
142
+
143
+ const report = finalState.static_report ?? {};
144
+ return formatStaticReport(report, scanId);
145
+ }
146
+
147
+ /** Recon — surface mapping / endpoint discovery. */
148
+ async function runRecon(args) {
149
+ const { api, token } = resolveConfig();
150
+ const body = JSON.stringify({
151
+ url: args.url,
152
+ max_depth: args.max_depth ?? 2,
153
+ max_pages: args.max_pages ?? 30,
154
+ });
155
+
156
+ const { finalState, scanId, error } = await drainStream(
157
+ `${api}/scan/stream/recon`, token,
158
+ { "Content-Type": "application/json" }, body,
159
+ );
160
+
161
+ if (error) throw new Error(error);
162
+
163
+ const report = finalState.surface_report ?? {};
164
+ const endpoints = finalState.classified_endpoints ?? report.endpoints ?? [];
165
+ return formatReconReport(report, endpoints, scanId);
166
+ }
167
+
168
+ /** Header audit — security-header & cookie checks. */
169
+ async function runHeaderAudit(args) {
170
+ const { api, token } = resolveConfig();
171
+ const body = JSON.stringify({
172
+ url: args.url,
173
+ max_depth: args.max_depth ?? 2,
174
+ max_pages: args.max_pages ?? 30,
175
+ });
176
+
177
+ const { finalState, scanId, error } = await drainStream(
178
+ `${api}/scan/stream/full`, token,
179
+ { "Content-Type": "application/json" }, body,
180
+ );
181
+
182
+ if (error) throw new Error(error);
183
+
184
+ const auditReport = finalState.audit_report ?? {};
185
+ const surfaceReport = finalState.surface_report ?? {};
186
+ return formatHeaderReport(auditReport, surfaceReport, scanId);
187
+ }
188
+
189
+
190
+
191
+ // ── Formatters ───────────────────────────────────────────────────────────────
192
+
193
+ function formatStaticReport(report, scanId) {
194
+ const lines = [
195
+ "## SecuraAI Static Analysis Report",
196
+ "",
197
+ `**Target:** ${report.target ?? "unknown"}`,
198
+ `**Scan ID:** ${scanId ?? "n/a"}`,
199
+ `**Summary:** ${report.summary ?? "No summary available."}`,
200
+ "",
201
+ `| Metric | Count |`,
202
+ `|--------|-------|`,
203
+ `| Unique findings (after merge) | ${report.total_after_merge ?? 0} |`,
204
+ `| Confirmed vulnerabilities | ${report.confirmed_count ?? 0} |`,
205
+ `| Ruled out (false positives) | ${report.ruled_out_count ?? 0} |`,
206
+ `| Fixes generated | ${report.fixes_count ?? 0} |`,
207
+ `| With CodeQL taint path | ${report.with_taint_path_count ?? 0} |`,
208
+ "",
209
+ ];
210
+
211
+ const confirmed = report.confirmed_findings ?? [];
212
+ if (confirmed.length > 0) {
213
+ lines.push("### Confirmed Vulnerabilities", "");
214
+ confirmed.slice(0, 20).forEach((f, i) => {
215
+ const sev = (f.final_severity || f.severity || "info").toUpperCase();
216
+ const evidence = f.taint_path ? "taint path"
217
+ : f.multi_tool_confirmed ? "multi-tool"
218
+ : "single tool";
219
+ lines.push(
220
+ `**${i + 1}. [${sev}] ${f.category ?? f.rule_id}**`,
221
+ `- File: \`${f.file}:${f.line}\``,
222
+ `- Rule: \`${f.rule_id}\``,
223
+ `- Evidence: ${evidence}`,
224
+ f.triage_reason ? `- Reason: ${f.triage_reason}` : null,
225
+ "",
226
+ ).filter(Boolean);
227
+ });
228
+ if (confirmed.length > 20) lines.push(`_…and ${confirmed.length - 20} more_`, "");
229
+ } else {
230
+ lines.push("✅ No confirmed vulnerabilities found.", "");
231
+ }
232
+
233
+ return { content: [{ type: "text", text: lines.filter(l => l !== null).join("\n") }] };
234
+ }
235
+
236
+ function formatReconReport(report, endpoints, scanId) {
237
+ const lines = [
238
+ "## SecuraAI Recon Report",
239
+ "",
240
+ `**Target:** ${report.target_url ?? "unknown"}`,
241
+ `**Scan ID:** ${scanId ?? "n/a"}`,
242
+ `**Total Endpoints:** ${report.total_endpoints ?? endpoints.length}`,
243
+ `**Scanned At:** ${report.scan_timestamp ?? new Date().toISOString()}`,
244
+ "",
245
+ report.summary ? `### Summary\n\n${report.summary}\n` : null,
246
+ "### Endpoints Discovered",
247
+ "",
248
+ `| URL | Method | Type | Status |`,
249
+ `|-----|--------|------|--------|`,
250
+ ].filter(Boolean);
251
+
252
+ endpoints.slice(0, 50).forEach(ep => {
253
+ lines.push(
254
+ `| \`${ep.url}\` | ${ep.method ?? "GET"} | ${ep.endpoint_type ?? "unknown"} | ${ep.status_code ?? "-"} |`
255
+ );
256
+ });
257
+
258
+ if (endpoints.length > 50) lines.push(`\n_…and ${endpoints.length - 50} more endpoints_`);
259
+
260
+ return { content: [{ type: "text", text: lines.join("\n") }] };
261
+ }
262
+
263
+ function formatHeaderReport(audit, surface, scanId) {
264
+ const lines = [
265
+ "## SecuraAI Header Audit Report",
266
+ "",
267
+ `**Target:** ${audit.target_url ?? surface.target_url ?? "unknown"}`,
268
+ `**Scan ID:** ${scanId ?? "n/a"}`,
269
+ `**Total Findings:** ${audit.total_findings ?? 0}`,
270
+ "",
271
+ ];
272
+
273
+ const findings = audit.findings ?? [];
274
+ if (findings.length > 0) {
275
+ lines.push("### Findings", "");
276
+ findings.slice(0, 30).forEach((f, i) => {
277
+ const sev = (f.severity || "info").toUpperCase();
278
+ lines.push(
279
+ `**${i + 1}. [${sev}] ${f.header_name ?? f.category}**`,
280
+ `- URL: \`${f.url}\``,
281
+ `- Issue: ${f.issue ?? f.expected ?? ""}`,
282
+ f.recommendation ? `- Fix: ${f.recommendation}` : null,
283
+ "",
284
+ ).filter(Boolean);
285
+ });
286
+ } else {
287
+ lines.push("✅ No header misconfigurations found.", "");
288
+ }
289
+
290
+ return { content: [{ type: "text", text: lines.filter(l => l !== null).join("\n") }] };
291
+ }
292
+
293
+
294
+
295
+ // ── Tool definitions (shown to the AI) ───────────────────────────────────────
296
+
297
+ const TOOLS = [
298
+ {
299
+ name: "run_static_scan",
300
+ description: "Run SAST static analysis on a local code repository using Semgrep, Bearer, OSV-Scanner, Gitleaks and CodeQL. Returns confirmed vulnerabilities, false positives, and generated fixes.",
301
+ inputSchema: {
302
+ type: "object",
303
+ properties: {
304
+ target_path: {
305
+ type: "string",
306
+ description: "Absolute or relative path to the local repository/folder to scan. Can also be a GitHub URL (https://github.com/owner/repo) or 'owner/repo' shorthand.",
307
+ },
308
+ include_codeql: {
309
+ type: "boolean",
310
+ description: "Include CodeQL taint analysis (slower but finds deeper data-flow issues). Default: true.",
311
+ default: true,
312
+ },
313
+ codeql_language: {
314
+ type: "string",
315
+ description: "CodeQL extractor language (javascript, python, java, go, ruby, csharp, cpp). Leave empty to auto-detect.",
316
+ default: "",
317
+ },
318
+ max_triage: {
319
+ type: "number",
320
+ description: "Maximum number of merged findings to AI-triage. Default: 25.",
321
+ default: 25,
322
+ },
323
+ },
324
+ required: ["target_path"],
325
+ },
326
+ },
327
+ {
328
+ name: "run_recon",
329
+ description: "Crawl a web application and map its attack surface — discovering endpoints, classifying them (API, auth, static, etc.), and producing a surface report with an LLM-written summary.",
330
+ inputSchema: {
331
+ type: "object",
332
+ properties: {
333
+ url: {
334
+ type: "string",
335
+ description: "The target URL to crawl (e.g. https://example.com).",
336
+ },
337
+ max_depth: {
338
+ type: "number",
339
+ description: "Maximum crawl depth from the seed URL. Default: 2.",
340
+ default: 2,
341
+ },
342
+ max_pages: {
343
+ type: "number",
344
+ description: "Maximum number of pages to visit. Default: 30.",
345
+ default: 30,
346
+ },
347
+ },
348
+ required: ["url"],
349
+ },
350
+ },
351
+ {
352
+ name: "run_header_audit",
353
+ description: "Crawl a web application and audit its HTTP security headers and cookies against OWASP best practices (CSP, HSTS, X-Frame-Options, SameSite cookies, CORS, etc.).",
354
+ inputSchema: {
355
+ type: "object",
356
+ properties: {
357
+ url: {
358
+ type: "string",
359
+ description: "The target URL to audit (e.g. https://example.com).",
360
+ },
361
+ max_depth: {
362
+ type: "number",
363
+ description: "Maximum crawl depth. Default: 2.",
364
+ default: 2,
365
+ },
366
+ max_pages: {
367
+ type: "number",
368
+ description: "Maximum pages to audit. Default: 30.",
369
+ default: 30,
370
+ },
371
+ },
372
+ required: ["url"],
373
+ },
374
+ },
375
+ ];
376
+
377
+ // ── MCP Server setup ─────────────────────────────────────────────────────────
378
+
379
+ const server = new Server(
380
+ { name: "secura", version: "0.1.0" },
381
+ { capabilities: { tools: {} } },
382
+ );
383
+
384
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
385
+
386
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
387
+ const { name, arguments: args } = request.params;
388
+
389
+ try {
390
+ switch (name) {
391
+ case "run_static_scan": return await runStaticScan(args);
392
+ case "run_recon": return await runRecon(args);
393
+ case "run_header_audit": return await runHeaderAudit(args);
394
+ default:
395
+ return {
396
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
397
+ isError: true,
398
+ };
399
+ }
400
+ } catch (err) {
401
+ return {
402
+ content: [{ type: "text", text: `Error: ${err.message}` }],
403
+ isError: true,
404
+ };
405
+ }
406
+ });
407
+
408
+ // ── Start ─────────────────────────────────────────────────────────────────────
409
+
410
+ const transport = new StdioServerTransport();
411
+ await server.connect(transport);
412
+ // Server runs until the editor closes stdin.
package/package.json CHANGED
@@ -1,46 +1,54 @@
1
- {
2
- "name": "@authtrack/secura",
3
- "version": "0.1.0",
4
- "description": "AuthTrack static analysis (SAST) from the command line — streams a five-scanner scan (Semgrep, Bearer, OSV-Scanner, Gitleaks, CodeQL) from the AuthTrack backend and renders it live in your terminal.",
5
- "type": "module",
6
- "bin": {
7
- "secura": "bin/secura.mjs"
8
- },
9
- "engines": {
10
- "node": ">=18"
11
- },
12
- "files": [
13
- "bin",
14
- "src",
15
- "README.md"
16
- ],
17
- "keywords": [
18
- "sast",
19
- "static-analysis",
20
- "security",
21
- "semgrep",
22
- "codeql",
23
- "gitleaks",
24
- "osv-scanner",
25
- "bearer",
26
- "cli",
27
- "vulnerability",
28
- "authtrack"
29
- ],
30
- "repository": {
31
- "type": "git",
32
- "url": "git+https://github.com/ashleyalmeida07/Authtrack-Major-Project.git",
33
- "directory": "cli"
34
- },
35
- "homepage": "https://github.com/ashleyalmeida07/Authtrack-Major-Project#readme",
36
- "bugs": {
37
- "url": "https://github.com/ashleyalmeida07/Authtrack-Major-Project/issues"
38
- },
39
- "license": "MIT",
40
- "publishConfig": {
41
- "access": "public"
42
- },
43
- "scripts": {
44
- "secura": "node bin/secura.mjs"
45
- }
46
- }
1
+ {
2
+ "name": "@authtrack/secura",
3
+ "version": "0.1.1",
4
+ "description": "SecuraAI security scanner CLI and MCP server — static analysis, recon, header audit and injection testing via AI agents.",
5
+ "type": "module",
6
+ "bin": {
7
+ "secura": "bin/secura.mjs",
8
+ "secura-mcp": "bin/secura-mcp.mjs"
9
+ },
10
+ "engines": {
11
+ "node": ">=18"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "src",
16
+ "README.md"
17
+ ],
18
+ "dependencies": {
19
+ "@modelcontextprotocol/sdk": "^1.0.0"
20
+ },
21
+ "keywords": [
22
+ "sast",
23
+ "static-analysis",
24
+ "security",
25
+ "semgrep",
26
+ "codeql",
27
+ "gitleaks",
28
+ "osv-scanner",
29
+ "bearer",
30
+ "cli",
31
+ "vulnerability",
32
+ "mcp",
33
+ "model-context-protocol",
34
+ "securai",
35
+ "securaai"
36
+ ],
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/ashleyalmeida07/Authtrack-Major-Project.git",
40
+ "directory": "cli"
41
+ },
42
+ "homepage": "https://github.com/ashleyalmeida07/Authtrack-Major-Project#readme",
43
+ "bugs": {
44
+ "url": "https://github.com/ashleyalmeida07/Authtrack-Major-Project/issues"
45
+ },
46
+ "license": "MIT",
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "scripts": {
51
+ "secura": "node bin/secura.mjs",
52
+ "secura-mcp": "node bin/secura-mcp.mjs"
53
+ }
54
+ }
package/src/config.mjs CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  // Matches frontend/src/lib/api.ts (API_BASE) so the CLI and the web app default
10
10
  // to the same backend.
11
- const DEFAULT_API = "http://localhost:8000/api/v1";
11
+ const DEFAULT_API = "https://securaai-major-project.onrender.com/api/v1";
12
12
 
13
13
  export function resolveConfig(values) {
14
14
  const raw = values.api || process.env.SECURA_API || DEFAULT_API;