secufusion-mcp 1.0.4 → 1.0.6
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 +36 -38
- package/index.js +182 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
| Tool | Phase | What it does |
|
|
16
16
|
|---|---|---|
|
|
17
|
-
| `manage_feature_spec` | Planning & Execution | Creates/updates a `.current-task-spec.md` blueprint for
|
|
18
|
-
| `log_rejected_pattern` | Course Correction | Records bad patterns to `.rejected-patterns.json` so they
|
|
19
|
-
| `run_pre_pr_checks` | PR Handoff |
|
|
17
|
+
| `manage_feature_spec` | Planning & Execution | Creates/updates a `.current-task-spec.md` blueprint (works for single-repo or cross-repo tasks) |
|
|
18
|
+
| `log_rejected_pattern` | Course Correction | Records bad patterns to `.rejected-patterns.json` so they are never repeated |
|
|
19
|
+
| `run_pre_pr_checks` | PR Handoff | Discovers modified microservices across the workspace and gates PRs via AST-level linters + structural checks |
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -213,26 +213,23 @@ This writes to `.rejected-patterns.json`:
|
|
|
213
213
|
|
|
214
214
|
---
|
|
215
215
|
|
|
216
|
-
### 3.
|
|
217
|
-
|
|
218
|
-
Scans your entire codebase and produces a pass/fail report. **Must pass before raising a PR.**
|
|
219
|
-
|
|
220
|
-
**Parameters:**
|
|
216
|
+
### 3. run_pre_pr_checks
|
|
221
217
|
|
|
222
218
|
| Parameter | Type | Required | Description |
|
|
223
219
|
|---|---|---|---|
|
|
224
|
-
| `work_item_id` | string | Yes | Azure DevOps work item ID
|
|
220
|
+
| `work_item_id` | string | Yes | Azure DevOps work item ID |
|
|
225
221
|
| `root_dir` | string | No | Directory to scan (defaults to cwd) |
|
|
226
|
-
| `skip_checks` | array | No |
|
|
222
|
+
| `skip_checks` | array | No | `spec_boxes`, `console_logs`, `hardcoded_urls`, `flyway_migrations` |
|
|
227
223
|
|
|
228
|
-
**
|
|
224
|
+
**Guardrail checks:**
|
|
229
225
|
|
|
230
|
-
| Check |
|
|
226
|
+
| Check | Fails when |
|
|
231
227
|
|---|---|
|
|
232
|
-
| **SPEC** | `.current-task-spec.md` is missing
|
|
233
|
-
| **
|
|
234
|
-
| **SECURITY** | Hardcoded
|
|
235
|
-
| **
|
|
228
|
+
| **SPEC** | `.current-task-spec.md` is missing or has unchecked `- [ ]` boxes |
|
|
229
|
+
| **LINTING** | AST linters (ESLint, Checkstyle) fail in any discovered microservice. Fix natively, do not suppress! (Falls back to regex if no linters exist) |
|
|
230
|
+
| **SECURITY** | Hardcoded IPs or `uat.*` / `prod.*` / `staging.*` URLs in source or config files |
|
|
231
|
+
| **TENANT ISOLATION** | `*Repository.java` files contain query methods or `@Query` annotations that do NOT include `tenant` filtering |
|
|
232
|
+
| **FLYWAY** | `@Entity`-annotated Java files exist but no `V*__.sql` Flyway migrations found |
|
|
236
233
|
|
|
237
234
|
**Example — Run checks before PR:**
|
|
238
235
|
|
|
@@ -521,7 +518,7 @@ Timestamp: 2026-08-26T16:00:00.000Z
|
|
|
521
518
|
## PR Summary
|
|
522
519
|
- Work Item: #2847
|
|
523
520
|
- Spec: All acceptance criteria verified ✅
|
|
524
|
-
- Guardrails:
|
|
521
|
+
- Guardrails: tenant isolation secured, no debug logs, no hardcoded URLs, Flyway covered ✅
|
|
525
522
|
- Ready to raise PR 🚀
|
|
526
523
|
|
|
527
524
|
🚫 Rejected Patterns Reminder (1 on record)
|
|
@@ -538,9 +535,9 @@ Timestamp: 2026-08-26T16:00:00.000Z
|
|
|
538
535
|
• Flyway SQL migration created for every modified JPA @Entity
|
|
539
536
|
• API contract (OpenAPI / TS types) updated if endpoints changed
|
|
540
537
|
|
|
541
|
-
❌ [
|
|
542
|
-
•
|
|
543
|
-
•
|
|
538
|
+
❌ [LINTING] 2 linter error(s) across workspace — fix natively in their respective microservices:
|
|
539
|
+
• [ESLint] apps/admin-dashboard/src/components/MfaSetup.tsx:47 — Unexpected console statement (no-console)
|
|
540
|
+
• [Checkstyle/Maven] Build failed: [ERROR] AuditService.java:[112] Line contains System.out.println
|
|
544
541
|
|
|
545
542
|
## Passed
|
|
546
543
|
✅ [SECURITY] No hardcoded UAT/Prod IPs or environment URLs found.
|
|
@@ -585,32 +582,33 @@ Create `.agents/AGENTS.md` in your project root:
|
|
|
585
582
|
```markdown
|
|
586
583
|
# SecuFusion MCP Workflow Rules
|
|
587
584
|
|
|
588
|
-
You are an elite Senior Developer working on the SecuFusion
|
|
589
|
-
You have access to the `secufusion-mcp` MCP server. Follow these rules strictly.
|
|
585
|
+
You are an elite Senior Developer and Architect working on the SecuFusion workspace (containing multiple microservice repositories). You prioritize robust cross-service architecture, zero-trust security (tenant isolation), and flawless state management. You rely on standard AST-aware build tools (ESLint/Checkstyle) for code hygiene. You strictly follow project standards and always use the custom MCP tools provided.
|
|
590
586
|
|
|
591
587
|
## Phase 1 — Planning (TRIGGER: user assigns a task or work item)
|
|
592
|
-
- Do NOT write code immediately.
|
|
593
|
-
-
|
|
588
|
+
- Do NOT write code immediately. First, call `manage_feature_spec`.
|
|
589
|
+
- Determine which microservices are likely affected. Pass a `reference_file_path` from each to observe exact coding patterns.
|
|
590
|
+
- Pass the user's requirements into `task_description` to generate the workspace-level `.current-task-spec.md` blueprint.
|
|
594
591
|
- Wait for the spec to be generated before writing any code.
|
|
595
592
|
|
|
596
593
|
## Phase 2 — Execution (TRIGGER: as you complete work)
|
|
597
|
-
- Keep `.current-task-spec.md` updated as your source of truth.
|
|
598
|
-
-
|
|
599
|
-
- If resuming a session, always call `manage_feature_spec` with `action=read` first.
|
|
594
|
+
- Keep the `.current-task-spec.md` file updated as your source of truth.
|
|
595
|
+
- Whenever you finish a logical chunk, call `manage_feature_spec` with `update_content` to check off the `[ ]` boxes to `[x]`.
|
|
596
|
+
- If you are resuming a session, always call `manage_feature_spec` with `action=read` first to know exactly where you left off.
|
|
600
597
|
|
|
601
598
|
## Phase 3 — Course Correction (TRIGGER: user corrects you or rejects an approach)
|
|
602
599
|
- Immediately call `log_rejected_pattern`.
|
|
603
|
-
- Pass the bad `pattern` and the `reason
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
-
|
|
612
|
-
-
|
|
613
|
-
-
|
|
600
|
+
- Pass the bad `pattern` you used and the `reason`/correction the user provided.
|
|
601
|
+
- Always check `.rejected-patterns.json` implicitly before suggesting architectural choices to ensure you never repeat past mistakes.
|
|
602
|
+
|
|
603
|
+
## Phase 4 — PR Handoff (TRIGGER: user says "prepare PR", "finish up", or "run checks")
|
|
604
|
+
- Call `run_pre_pr_checks` with the Azure DevOps `work_item_id`. The tool will automatically discover all modified microservices in the workspace and run native linters, database script checks, and strict tenant-isolation scanners.
|
|
605
|
+
- If the tool throws an error for a specific repository (e.g., missing Flyway SQL migration, missing `tenantId` in a query, or a linter failing), YOU MUST navigate to that specific microservice, FIX THE ERROR in the codebase natively, and run the tool again until the entire workspace passes. Do not suppress warnings.
|
|
606
|
+
|
|
607
|
+
## Guardrails (enforce always, no exceptions)
|
|
608
|
+
- TENANT SAFETY IS AUTOMATED: The PR Gatekeeper actively scans all `*Repository.java` files. If you write a database query (derived method or `@Query`) that does not explicitly filter by `tenantId` or contain the word `tenant`, the PR will be blocked. Write secure, tenant-isolated queries on your first attempt.
|
|
609
|
+
- Do not ignore linter errors. AST-level tools (ESLint, Checkstyle/Maven) are the source of truth for hygiene. Fix them natively.
|
|
610
|
+
- Never hardcode UAT/Prod IPs or URLs. Use environment variables or configuration properties.
|
|
611
|
+
- If you modify a JPA `@Entity` in any backend repo, you MUST create the corresponding Flyway `.sql` migration script before running PR checks.
|
|
614
612
|
```
|
|
615
613
|
|
|
616
614
|
### How it all wires together
|
package/index.js
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* Provides developer-workflow tooling for the SecuFusion platform:
|
|
6
6
|
* - manage_feature_spec : create / update task blueprints (.current-task-spec.md)
|
|
7
7
|
* - log_rejected_pattern : record bad patterns so they are never repeated (.rejected-patterns.json)
|
|
8
|
-
* - run_pre_pr_checks : gate PRs
|
|
8
|
+
* - run_pre_pr_checks : gate PRs via AST-level linters (ESLint / Maven Checkstyle) + structural checks
|
|
9
9
|
*
|
|
10
10
|
* Guardrails enforced:
|
|
11
|
-
* 1.
|
|
12
|
-
* 2.
|
|
11
|
+
* 1. TENANT SAFETY AUTOMATED: Active scanning of *Repository.java for tenantId scoping.
|
|
12
|
+
* 2. Code hygiene (console.log, unused vars, formatting) enforced via ESLint / Checkstyle — not regex.
|
|
13
13
|
* 3. No hardcoded UAT/Prod IPs or environment URLs.
|
|
14
14
|
* 4. Every modified JPA @Entity must have a matching Flyway SQL migration.
|
|
15
15
|
*/
|
|
@@ -18,6 +18,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
import fs from "fs";
|
|
20
20
|
import path from "path";
|
|
21
|
+
import { execSync } from "child_process";
|
|
21
22
|
// ─────────────────────────────────────────────
|
|
22
23
|
// Constants / helpers
|
|
23
24
|
// ─────────────────────────────────────────────
|
|
@@ -93,7 +94,7 @@ function appendSessionLog(existing, update) {
|
|
|
93
94
|
// ─────────────────────────────────────────────
|
|
94
95
|
// run_pre_pr_checks helpers
|
|
95
96
|
// ─────────────────────────────────────────────
|
|
96
|
-
const
|
|
97
|
+
const CONSOLE_LOG_REGEX = [
|
|
97
98
|
{ regex: /console\.log\s*\(/g, label: "console.log()", ext: [".ts", ".tsx", ".js", ".jsx"] },
|
|
98
99
|
{ regex: /System\.out\.println\s*\(/g, label: "System.out.println()", ext: [".java"] },
|
|
99
100
|
];
|
|
@@ -127,9 +128,71 @@ function checkSpecFile() {
|
|
|
127
128
|
}
|
|
128
129
|
return { unchecked, missing: false };
|
|
129
130
|
}
|
|
130
|
-
|
|
131
|
+
// ── AST-level linting via ESLint (preferred over regex for JS/TS) ─────────────
|
|
132
|
+
function runEslint(rootDir) {
|
|
133
|
+
// Check if ESLint is configured in the project
|
|
134
|
+
const eslintConfigs = [".eslintrc", ".eslintrc.js", ".eslintrc.json", ".eslintrc.yml",
|
|
135
|
+
".eslintrc.yaml", "eslint.config.js", "eslint.config.mjs"];
|
|
136
|
+
const hasEslintConfig = eslintConfigs.some((f) => fs.existsSync(path.join(rootDir, f)));
|
|
137
|
+
const hasPkgEslint = (() => {
|
|
138
|
+
const pkg = readFileSafe(path.join(rootDir, "package.json"));
|
|
139
|
+
if (!pkg)
|
|
140
|
+
return false;
|
|
141
|
+
try {
|
|
142
|
+
return !!(JSON.parse(pkg).eslintConfig);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
})();
|
|
148
|
+
if (!hasEslintConfig && !hasPkgEslint)
|
|
149
|
+
return { violations: [], available: false };
|
|
150
|
+
try {
|
|
151
|
+
execSync("npx eslint . --format json --max-warnings=0 --no-error-on-unmatched-pattern", { cwd: rootDir, stdio: "pipe" });
|
|
152
|
+
return { violations: [], available: true };
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
const raw = e.stdout?.toString() ?? "";
|
|
156
|
+
try {
|
|
157
|
+
const results = JSON.parse(raw);
|
|
158
|
+
const violations = [];
|
|
159
|
+
for (const file of results) {
|
|
160
|
+
for (const msg of file.messages) {
|
|
161
|
+
if (msg.severity > 0) {
|
|
162
|
+
violations.push(`[ESLint] ${path.relative(rootDir, file.filePath)}:${msg.line} — ${msg.message}${msg.ruleId ? ` (${msg.ruleId})` : ""}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { violations, available: true };
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// ESLint output not JSON — return raw stderr summary
|
|
170
|
+
const stderr = e.stderr?.toString() ?? "";
|
|
171
|
+
return { violations: stderr ? [`[ESLint] ${stderr.slice(0, 400)}`] : [], available: true };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// ── Maven + Checkstyle for Java/Spring projects ───────────────────────────────
|
|
176
|
+
function runCheckstyle(rootDir) {
|
|
177
|
+
const hasPom = fs.existsSync(path.join(rootDir, "pom.xml"));
|
|
178
|
+
if (!hasPom)
|
|
179
|
+
return { violations: [], available: false };
|
|
180
|
+
try {
|
|
181
|
+
execSync("mvn checkstyle:check -q --no-transfer-progress", { cwd: rootDir, stdio: "pipe" });
|
|
182
|
+
return { violations: [], available: true };
|
|
183
|
+
}
|
|
184
|
+
catch (e) {
|
|
185
|
+
const output = ((e.stdout?.toString() ?? "") + (e.stderr?.toString() ?? "")).slice(0, 1200);
|
|
186
|
+
return {
|
|
187
|
+
violations: [`[Checkstyle/Maven] Build failed:\n${output}`],
|
|
188
|
+
available: true,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// ── Regex fallback — used only when no linter is configured ──────────────────
|
|
193
|
+
function checkConsoleLogsRegex(rootDir) {
|
|
131
194
|
const violations = [];
|
|
132
|
-
for (const { regex, label, ext } of
|
|
195
|
+
for (const { regex, label, ext } of CONSOLE_LOG_REGEX) {
|
|
133
196
|
const files = walkDir(rootDir, ext);
|
|
134
197
|
for (const file of files) {
|
|
135
198
|
const content = readFileSafe(file);
|
|
@@ -161,6 +224,53 @@ function checkHardcodedUrls(rootDir) {
|
|
|
161
224
|
}
|
|
162
225
|
return violations;
|
|
163
226
|
}
|
|
227
|
+
// ── Workspace / Microservice Discovery ───────────────────────────────────────
|
|
228
|
+
function findProjectRoots(dir, depth = 0) {
|
|
229
|
+
if (depth > 3)
|
|
230
|
+
return []; // Limit depth to avoid scanning massive trees
|
|
231
|
+
if (!fs.existsSync(dir))
|
|
232
|
+
return [];
|
|
233
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
234
|
+
const hasPackageJson = entries.some(e => e.name === "package.json");
|
|
235
|
+
const hasPomXml = entries.some(e => e.name === "pom.xml");
|
|
236
|
+
const roots = [];
|
|
237
|
+
if (hasPackageJson || hasPomXml) {
|
|
238
|
+
roots.push(dir);
|
|
239
|
+
}
|
|
240
|
+
for (const entry of entries) {
|
|
241
|
+
if (entry.isDirectory()) {
|
|
242
|
+
if (["node_modules", ".git", "dist", "build", "target", ".idea"].includes(entry.name))
|
|
243
|
+
continue;
|
|
244
|
+
roots.push(...findProjectRoots(path.join(dir, entry.name), depth + 1));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return [...new Set(roots)];
|
|
248
|
+
}
|
|
249
|
+
// ── Automated Tenant Isolation Scanner ────────────────────────────────────────
|
|
250
|
+
function checkTenantIsolation(rootDir) {
|
|
251
|
+
const violations = [];
|
|
252
|
+
const files = walkDir(rootDir, [".java"]).filter(f => f.endsWith("Repository.java"));
|
|
253
|
+
const queryMethodRegex = /\b(?:find|get|read|query|search|stream|count|exists|delete|remove)(?:All)?By[A-Z0-9]/;
|
|
254
|
+
const atQueryRegex = /@Query\s*\(/;
|
|
255
|
+
for (const file of files) {
|
|
256
|
+
const content = readFileSafe(file);
|
|
257
|
+
if (!content)
|
|
258
|
+
continue;
|
|
259
|
+
const lines = content.split("\n");
|
|
260
|
+
for (let i = 0; i < lines.length; i++) {
|
|
261
|
+
const line = lines[i];
|
|
262
|
+
if (queryMethodRegex.test(line) || atQueryRegex.test(line)) {
|
|
263
|
+
// Look at current line and the next to handle simple wrapping
|
|
264
|
+
const combinedContext = line + (lines[i + 1] || "");
|
|
265
|
+
if (!/tenant/i.test(combinedContext)) {
|
|
266
|
+
violations.push(`Missing tenant isolation in ${path.relative(rootDir, file)}:${i + 1} — query lacks 'tenantId':\n` +
|
|
267
|
+
` ${line.trim()}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return violations;
|
|
273
|
+
}
|
|
164
274
|
function checkFlywayMigrations(rootDir) {
|
|
165
275
|
const warnings = [];
|
|
166
276
|
const javaFiles = walkDir(rootDir, [".java"]);
|
|
@@ -195,10 +305,9 @@ const server = new McpServer({
|
|
|
195
305
|
version: "1.0.0",
|
|
196
306
|
});
|
|
197
307
|
// ─── Tool 1: manage_feature_spec ─────────────────────────────────────────────
|
|
198
|
-
server.tool("manage_feature_spec", "Create or update the .current-task-spec.md
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"so the spec generator can observe existing coding patterns.", {
|
|
308
|
+
server.tool("manage_feature_spec", "Create or update the .current-task-spec.md blueprint for the current feature. " +
|
|
309
|
+
"Use this during the Planning and Execution phases. " +
|
|
310
|
+
"Works for both single-repo and cross-repo workspace tasks.", {
|
|
202
311
|
task_description: z
|
|
203
312
|
.string()
|
|
204
313
|
.optional()
|
|
@@ -379,10 +488,11 @@ server.tool("log_rejected_pattern", "Record a coding pattern that was rejected b
|
|
|
379
488
|
};
|
|
380
489
|
});
|
|
381
490
|
// ─── Tool 3: run_pre_pr_checks ───────────────────────────────────────────────
|
|
382
|
-
server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks
|
|
383
|
-
"
|
|
384
|
-
"and Flyway migration coverage for JPA entities. " +
|
|
385
|
-
"
|
|
491
|
+
server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks across the workspace. " +
|
|
492
|
+
"Automatically discovers modified microservices and runs native linters, database script checks, and strict tenant-isolation scanners. " +
|
|
493
|
+
"Validates: spec checkboxes, hardcoded IPs/URLs, tenantId in Repositories, and Flyway migration coverage for JPA entities. " +
|
|
494
|
+
"Linter/isolation errors must be fixed natively — do not suppress warnings to pass the gate. " +
|
|
495
|
+
"MUST pass with zero errors before a PR is raised.", {
|
|
386
496
|
work_item_id: z
|
|
387
497
|
.string()
|
|
388
498
|
.describe("Azure DevOps work item ID to include in the PR summary."),
|
|
@@ -394,6 +504,7 @@ server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks aga
|
|
|
394
504
|
.array(z.enum([
|
|
395
505
|
"spec_boxes",
|
|
396
506
|
"console_logs",
|
|
507
|
+
"tenant_isolation",
|
|
397
508
|
"hardcoded_urls",
|
|
398
509
|
"flyway_migrations",
|
|
399
510
|
]))
|
|
@@ -422,21 +533,70 @@ server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks aga
|
|
|
422
533
|
else {
|
|
423
534
|
warnings.push("⚠️ [SPEC] Spec checkbox check SKIPPED (manually bypassed).");
|
|
424
535
|
}
|
|
425
|
-
// ── 2.
|
|
536
|
+
// ── 2. AST-level linting (Workspace Discovery → ESLint/Checkstyle → regex fallback)
|
|
426
537
|
if (!skip_checks.includes("console_logs")) {
|
|
427
|
-
|
|
428
|
-
if (
|
|
429
|
-
|
|
430
|
-
|
|
538
|
+
let projectRoots = findProjectRoots(scanRoot);
|
|
539
|
+
if (projectRoots.length === 0)
|
|
540
|
+
projectRoots.push(scanRoot);
|
|
541
|
+
let anyLinterAvailable = false;
|
|
542
|
+
const allLintViolations = [];
|
|
543
|
+
const usedLinters = new Set();
|
|
544
|
+
for (const pRoot of projectRoots) {
|
|
545
|
+
const eslint = runEslint(pRoot);
|
|
546
|
+
const checkstyle = runCheckstyle(pRoot);
|
|
547
|
+
if (eslint.available) {
|
|
548
|
+
anyLinterAvailable = true;
|
|
549
|
+
usedLinters.add("ESLint");
|
|
550
|
+
allLintViolations.push(...eslint.violations);
|
|
551
|
+
}
|
|
552
|
+
if (checkstyle.available) {
|
|
553
|
+
anyLinterAvailable = true;
|
|
554
|
+
usedLinters.add("Checkstyle");
|
|
555
|
+
allLintViolations.push(...checkstyle.violations);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (anyLinterAvailable) {
|
|
559
|
+
// AST linters found and ran — they are the source of truth
|
|
560
|
+
if (allLintViolations.length > 0) {
|
|
561
|
+
errors.push(`❌ [LINTING] ${allLintViolations.length} linter error(s) across workspace — fix natively in their respective microservices:\n` +
|
|
562
|
+
allLintViolations.map((v) => ` • ${v}`).join("\n"));
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
passed.push(`✅ [LINTING] AST-level linting passed across workspace (${Array.from(usedLinters).join(" + ")}).`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
// No linters configured — fall back to regex scan with a warning
|
|
570
|
+
const regexViolations = checkConsoleLogsRegex(scanRoot);
|
|
571
|
+
if (regexViolations.length > 0) {
|
|
572
|
+
errors.push(`❌ [LOGGING] ${regexViolations.length} prohibited log statement(s) found via regex fallback\n` +
|
|
573
|
+
` (No ESLint/Checkstyle config detected in any workspace project — configure a linter for AST-level accuracy):\n` +
|
|
574
|
+
regexViolations.map((v) => ` • ${v}`).join("\n"));
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
warnings.push("⚠️ [LOGGING] No ESLint/Checkstyle config found in workspace — used regex fallback. " +
|
|
578
|
+
"Consider adding a linter for AST-level hygiene.");
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
warnings.push("⚠️ [LINTING] Linter check SKIPPED (manually bypassed).");
|
|
584
|
+
}
|
|
585
|
+
// ── 3. Tenant Isolation Scanner ──────────────────────────────────────────
|
|
586
|
+
if (!skip_checks.includes("tenant_isolation")) {
|
|
587
|
+
const tenantViolations = checkTenantIsolation(scanRoot);
|
|
588
|
+
if (tenantViolations.length > 0) {
|
|
589
|
+
errors.push(`❌ [SECURITY] Tenant Isolation Failed: Found ${tenantViolations.length} query method(s) in *Repository.java without tenant scoping:\n` +
|
|
590
|
+
tenantViolations.map((v) => ` • ${v}`).join("\n"));
|
|
431
591
|
}
|
|
432
592
|
else {
|
|
433
|
-
passed.push("✅ [
|
|
593
|
+
passed.push("✅ [SECURITY] Tenant isolation active: All database queries correctly scoped with tenantId.");
|
|
434
594
|
}
|
|
435
595
|
}
|
|
436
596
|
else {
|
|
437
|
-
warnings.push("⚠️ [
|
|
597
|
+
warnings.push("⚠️ [SECURITY] Tenant isolation check SKIPPED (manually bypassed).");
|
|
438
598
|
}
|
|
439
|
-
// ──
|
|
599
|
+
// ── 4. Hardcoded URL check (regex) ───────────────────────────────────────
|
|
440
600
|
if (!skip_checks.includes("hardcoded_urls")) {
|
|
441
601
|
const urlViolations = checkHardcodedUrls(scanRoot);
|
|
442
602
|
if (urlViolations.length > 0) {
|