@authtrack/secura 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.
@@ -30,7 +30,7 @@
30
30
  * }
31
31
  */
32
32
 
33
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
33
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
34
34
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
35
35
  import {
36
36
  CallToolRequestSchema,
@@ -39,11 +39,11 @@ import {
39
39
 
40
40
  // ── Config ───────────────────────────────────────────────────────────────────
41
41
 
42
- const DEFAULT_API = "https://securaai-major-project.onrender.com/api/v1";
42
+ const DEFAULT_API = "https://52.91.8.40.nip.io/api/v1";
43
43
 
44
44
  function resolveConfig() {
45
- const api = (process.env.SECURA_API || DEFAULT_API).replace(/\/+$/, "");
46
- const token = process.env.SECURA_TOKEN || "";
45
+ const api = (process.env.SECURA_API || DEFAULT_API).replace(/\/+$/, "");
46
+ const token = process.env.SECURA_TOKEN || "";
47
47
  return { api, token };
48
48
  }
49
49
 
@@ -57,8 +57,8 @@ function authHeaders(token, extra = {}) {
57
57
 
58
58
  async function* readSse(res) {
59
59
  const decoder = new TextDecoder();
60
- const reader = res.body.getReader();
61
- let buffer = "";
60
+ const reader = res.body.getReader();
61
+ let buffer = "";
62
62
 
63
63
  const emit = function* (frame) {
64
64
  for (const line of frame.split("\n")) {
@@ -89,7 +89,7 @@ async function drainStream(url, token, headers, body) {
89
89
  let res;
90
90
  try {
91
91
  res = await fetch(url, {
92
- method: "POST",
92
+ method: "POST",
93
93
  headers: authHeaders(token, headers),
94
94
  body,
95
95
  });
@@ -103,8 +103,8 @@ async function drainStream(url, token, headers, body) {
103
103
  }
104
104
 
105
105
  const accumulated = {};
106
- let scanId = null;
107
- let lastError = null;
106
+ let scanId = null;
107
+ let lastError = null;
108
108
 
109
109
  for await (const evt of readSse(res)) {
110
110
  if (evt.event === "node_update" && evt.state) {
@@ -127,10 +127,10 @@ async function drainStream(url, token, headers, body) {
127
127
  async function runStaticScan(args) {
128
128
  const { api, token } = resolveConfig();
129
129
  const body = JSON.stringify({
130
- target_path: args.target_path,
131
- include_codeql: args.include_codeql ?? true,
130
+ target_path: args.target_path,
131
+ include_codeql: args.include_codeql ?? true,
132
132
  codeql_language: args.codeql_language ?? "",
133
- max_triage: args.max_triage ?? 25,
133
+ max_triage: args.max_triage ?? 25,
134
134
  });
135
135
 
136
136
  const { finalState, scanId, error } = await drainStream(
@@ -148,7 +148,7 @@ async function runStaticScan(args) {
148
148
  async function runRecon(args) {
149
149
  const { api, token } = resolveConfig();
150
150
  const body = JSON.stringify({
151
- url: args.url,
151
+ url: args.url,
152
152
  max_depth: args.max_depth ?? 2,
153
153
  max_pages: args.max_pages ?? 30,
154
154
  });
@@ -160,7 +160,7 @@ async function runRecon(args) {
160
160
 
161
161
  if (error) throw new Error(error);
162
162
 
163
- const report = finalState.surface_report ?? {};
163
+ const report = finalState.surface_report ?? {};
164
164
  const endpoints = finalState.classified_endpoints ?? report.endpoints ?? [];
165
165
  return formatReconReport(report, endpoints, scanId);
166
166
  }
@@ -169,7 +169,7 @@ async function runRecon(args) {
169
169
  async function runHeaderAudit(args) {
170
170
  const { api, token } = resolveConfig();
171
171
  const body = JSON.stringify({
172
- url: args.url,
172
+ url: args.url,
173
173
  max_depth: args.max_depth ?? 2,
174
174
  max_pages: args.max_pages ?? 30,
175
175
  });
@@ -181,8 +181,8 @@ async function runHeaderAudit(args) {
181
181
 
182
182
  if (error) throw new Error(error);
183
183
 
184
- const auditReport = finalState.audit_report ?? {};
185
- const surfaceReport = finalState.surface_report ?? {};
184
+ const auditReport = finalState.audit_report ?? {};
185
+ const surfaceReport = finalState.surface_report ?? {};
186
186
  return formatHeaderReport(auditReport, surfaceReport, scanId);
187
187
  }
188
188
 
@@ -212,10 +212,10 @@ function formatStaticReport(report, scanId) {
212
212
  if (confirmed.length > 0) {
213
213
  lines.push("### Confirmed Vulnerabilities", "");
214
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";
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
219
  lines.push(
220
220
  `**${i + 1}. [${sev}] ${f.category ?? f.rule_id}**`,
221
221
  `- File: \`${f.file}:${f.line}\``,
@@ -296,77 +296,77 @@ function formatHeaderReport(audit, surface, scanId) {
296
296
 
297
297
  const TOOLS = [
298
298
  {
299
- name: "run_static_scan",
299
+ name: "run_static_scan",
300
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
301
  inputSchema: {
302
302
  type: "object",
303
303
  properties: {
304
304
  target_path: {
305
- type: "string",
305
+ type: "string",
306
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
307
  },
308
308
  include_codeql: {
309
- type: "boolean",
309
+ type: "boolean",
310
310
  description: "Include CodeQL taint analysis (slower but finds deeper data-flow issues). Default: true.",
311
- default: true,
311
+ default: true,
312
312
  },
313
313
  codeql_language: {
314
- type: "string",
314
+ type: "string",
315
315
  description: "CodeQL extractor language (javascript, python, java, go, ruby, csharp, cpp). Leave empty to auto-detect.",
316
- default: "",
316
+ default: "",
317
317
  },
318
318
  max_triage: {
319
- type: "number",
319
+ type: "number",
320
320
  description: "Maximum number of merged findings to AI-triage. Default: 25.",
321
- default: 25,
321
+ default: 25,
322
322
  },
323
323
  },
324
324
  required: ["target_path"],
325
325
  },
326
326
  },
327
327
  {
328
- name: "run_recon",
328
+ name: "run_recon",
329
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
330
  inputSchema: {
331
331
  type: "object",
332
332
  properties: {
333
333
  url: {
334
- type: "string",
334
+ type: "string",
335
335
  description: "The target URL to crawl (e.g. https://example.com).",
336
336
  },
337
337
  max_depth: {
338
- type: "number",
338
+ type: "number",
339
339
  description: "Maximum crawl depth from the seed URL. Default: 2.",
340
- default: 2,
340
+ default: 2,
341
341
  },
342
342
  max_pages: {
343
- type: "number",
343
+ type: "number",
344
344
  description: "Maximum number of pages to visit. Default: 30.",
345
- default: 30,
345
+ default: 30,
346
346
  },
347
347
  },
348
348
  required: ["url"],
349
349
  },
350
350
  },
351
351
  {
352
- name: "run_header_audit",
352
+ name: "run_header_audit",
353
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
354
  inputSchema: {
355
355
  type: "object",
356
356
  properties: {
357
357
  url: {
358
- type: "string",
358
+ type: "string",
359
359
  description: "The target URL to audit (e.g. https://example.com).",
360
360
  },
361
361
  max_depth: {
362
- type: "number",
362
+ type: "number",
363
363
  description: "Maximum crawl depth. Default: 2.",
364
- default: 2,
364
+ default: 2,
365
365
  },
366
366
  max_pages: {
367
- type: "number",
367
+ type: "number",
368
368
  description: "Maximum pages to audit. Default: 30.",
369
- default: 30,
369
+ default: 30,
370
370
  },
371
371
  },
372
372
  required: ["url"],
@@ -388,8 +388,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
388
388
 
389
389
  try {
390
390
  switch (name) {
391
- case "run_static_scan": return await runStaticScan(args);
392
- case "run_recon": return await runRecon(args);
391
+ case "run_static_scan": return await runStaticScan(args);
392
+ case "run_recon": return await runRecon(args);
393
393
  case "run_header_audit": return await runHeaderAudit(args);
394
394
  default:
395
395
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authtrack/secura",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "SecuraAI security scanner CLI and MCP server — static analysis, recon, header audit and injection testing via AI agents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -51,4 +51,4 @@
51
51
  "secura": "node bin/secura.mjs",
52
52
  "secura-mcp": "node bin/secura-mcp.mjs"
53
53
  }
54
- }
54
+ }
package/src/config.mjs CHANGED
@@ -1,33 +1,33 @@
1
- /**
2
- * Resolve where the CLI talks to and how.
3
- *
4
- * The backend's scan router has no auth, so the token is optional; it is sent
5
- * as a bearer header only when present, so this works unchanged if auth is
6
- * added later.
7
- */
8
-
9
- // Matches frontend/src/lib/api.ts (API_BASE) so the CLI and the web app default
10
- // to the same backend.
11
- const DEFAULT_API = "https://securaai-major-project.onrender.com/api/v1";
12
-
13
- export function resolveConfig(values) {
14
- const raw = values.api || process.env.SECURA_API || DEFAULT_API;
15
- const api = raw.replace(/\/+$/, ""); // trim trailing slashes
16
- const token = values.token || process.env.SECURA_TOKEN || "";
17
- return { api, token };
18
- }
19
-
20
- /**
21
- * True when the API base points at this machine. A local path target can then
22
- * be scanned in place (sent as `target_path`); otherwise it must be uploaded.
23
- */
24
- export function isLocalBackend(api) {
25
- try {
26
- const { hostname } = new URL(api);
27
- return ["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"].includes(hostname);
28
- } catch {
29
- // An unparseable base is treated as local — the request will fail loudly
30
- // either way, and this keeps the default (no upload) for odd inputs.
31
- return true;
32
- }
33
- }
1
+ /**
2
+ * Resolve where the CLI talks to and how.
3
+ *
4
+ * The backend's scan router has no auth, so the token is optional; it is sent
5
+ * as a bearer header only when present, so this works unchanged if auth is
6
+ * added later.
7
+ */
8
+
9
+ // Matches frontend/src/lib/api.ts (API_BASE) so the CLI and the web app default
10
+ // to the same backend.
11
+ const DEFAULT_API = "https://52.91.8.40.nip.io/api/v1";
12
+
13
+ export function resolveConfig(values) {
14
+ const raw = values.api || process.env.SECURA_API || DEFAULT_API;
15
+ const api = raw.replace(/\/+$/, ""); // trim trailing slashes
16
+ const token = values.token || process.env.SECURA_TOKEN || "";
17
+ return { api, token };
18
+ }
19
+
20
+ /**
21
+ * True when the API base points at this machine. A local path target can then
22
+ * be scanned in place (sent as `target_path`); otherwise it must be uploaded.
23
+ */
24
+ export function isLocalBackend(api) {
25
+ try {
26
+ const { hostname } = new URL(api);
27
+ return ["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"].includes(hostname);
28
+ } catch {
29
+ // An unparseable base is treated as local — the request will fail loudly
30
+ // either way, and this keeps the default (no upload) for odd inputs.
31
+ return true;
32
+ }
33
+ }