Skip to content

fix(ci): resolve failing workflows across rust, web, and vscode#2

Merged
chojuninengu merged 8 commits intomainfrom
feat/mvp-foundation
Apr 4, 2026
Merged

fix(ci): resolve failing workflows across rust, web, and vscode#2
chojuninengu merged 8 commits intomainfrom
feat/mvp-foundation

Conversation

@chojuninengu
Copy link
Copy Markdown
Member

@chojuninengu chojuninengu commented Apr 4, 2026

Summary by CodeRabbit

  • New Features

    • Interactive web scan UI with run/scan flow, results view, and dashboard landing with stats
    • REST API server for scan requests
    • Improved SAST and secrets detection with richer finding metadata
  • Documentation

    • Added initial changelog and updated project roadmap/status
  • Style

    • Integrated Tailwind-based dark design system and global UI components
  • Chores

    • Project workspace and web tooling updates (lint, typecheck, test scripts, config)

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

Warning

Rate limit exceeded

@chojuninengu has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 44 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 16 minutes and 44 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a00338cd-cd36-4f72-80c0-ae3688ef37bf

📥 Commits

Reviewing files that changed from the base of the PR and between dfc9e5f and 0c2432e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • Cargo.toml
  • apps/web/package.json
  • apps/web/src/lib/api.ts
  • apps/web/src/routes/+layout.svelte
  • apps/web/src/routes/+page.svelte
  • apps/web/src/routes/scan/+page.svelte
  • crates/scanner/src/engines/sast.rs
  • crates/scanner/src/engines/secrets.rs
  • crates/scanner/src/language.rs
  • crates/server/src/main.rs
  • extensions/vscode/package.json
📝 Walkthrough

Walkthrough

Adds a new Axum REST API crate and SvelteKit frontend, implements a regex-based SAST engine, extends finding structs with an optional description, updates workspace and frontend tooling (Tailwind, Vitest, ESLint), and updates documentation and changelog. (<=50 words)

Changes

Cohort / File(s) Summary
Workspace & Docs
/.gitignore, AGENTS.md, CHANGELOG.md, Cargo.toml
Stop ignoring Cargo.lock; add .svelte-kit ignore entry; document Phase 1–2 completion and initial 0.1.0-mvp changelog; add crates/server to workspace members.
Server crate (Axum)
crates/server/Cargo.toml, crates/server/src/main.rs
Add new zenvra-server crate with Axum-based HTTP server, /health and POST /api/v1/scan endpoints, request DTO ScanRequest, CORS, logging, and scan invocation via scanner library.
Scanner core & engines
crates/scanner/src/engines/sast.rs, crates/scanner/src/engines/secrets.rs, crates/scanner/src/finding.rs, crates/scanner/src/engine.rs, crates/scanner/src/engines/...
Replace SAST stub with regex-based rules and tests (MD5/SHA1, SQLi, OS command injection, XSS, localhost); add description: Option<String> to RawFinding and Finding; minor import reorderings across engine/AI files.
Frontend toolchain & config
apps/web/package.json, apps/web/vite.config.ts, apps/web/svelte.config.js, apps/web/eslint.config.js, apps/web/tsconfig.json
Add Tailwind/Vitest/ESLint tooling and pnpm packageManager entry; add Vite config enabling Tailwind + SvelteKit; remove Svelte preprocess; add TS config and ESLint flat config.
Frontend assets & styles
apps/web/src/app.css, apps/web/src/app.html, apps/web/vite.config.ts
Add Tailwind import, design tokens, utility classes (.glass, .glass-card, .btn-*, .text-gradient), scrollbar/selection styles, and base HTML template for SvelteKit.
Frontend pages & layout
apps/web/src/routes/+layout.svelte, apps/web/src/routes/+layout.ts, apps/web/src/routes/+page.svelte, apps/web/src/routes/scan/+page.svelte
Replace placeholder with full app shell (sidebar, header), disable SSR/prerender for layout, add landing page with stats/recent activity/providers, and add scan page that POSTs to http://localhost:8080/api/v1/scan and renders findings with severity/CWE details.
Frontend tests & linting
apps/web/src/lib/api.test.ts, apps/web/eslint.config.js
Add a basic Vitest test asserting true===true; add project ESLint flat config for web.
Extensions & editor config
extensions/vscode/eslint.config.js, extensions/vscode/package.json
Add VS Code ESLint flat config and update extension package devDependencies and scripts (reformatting + added lint tooling entries).
CLI / Misc refactors
crates/cli/src/main.rs, several crates/scanner/src/ai/*.rs and engine import lines
Small formatting and import-order-only changes; no behavioral changes.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Web as SvelteKit Frontend
    participant Server as Axum REST API
    participant Scanner as Scanner Engine

    User->>Web: Enter code & click "Run Scan"
    Web->>Server: POST /api/v1/scan (code, language, engines, ai_config)
    Server->>Server: Build ScanConfig from request
    Server->>Scanner: zenvra_scanner::scan(config)
    par SAST Engine
        Scanner->>Scanner: Apply regex rules (MD5, SQLi, OS cmd, XSS, etc.)
    and Secrets Engine
        Scanner->>Scanner: Detect secrets patterns
    end
    Scanner-->>Server: Vec<Finding> (JSON-serializable)
    Server-->>Web: JSON array of findings
    Web->>User: Render findings with severity/CWE and suggested fixes
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly Related PRs

Suggested Reviewers

  • Nkwenti-Severian-Ndongtsop

Poem

🐰 I hopped through code with tiny paws so fleet,
Added servers, scans, and styles both clean and neat.
Regex ears twitch for MD5 and SQL's bite,
Svelte lights the UI, Tailwind paints delight.
Phase one and two—now onward we leap!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'fix(ci): resolve failing workflows' does not match the actual changeset. The changes are predominantly feature additions (new Axum server, SvelteKit web app, SAST engine implementation, changelog, and configuration files), not CI/workflow fixes. Revise the title to accurately reflect the main objective: something like 'feat: implement MVP foundation with Axum API server and SvelteKit dashboard' would better represent the scope of feature additions.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mvp-foundation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Implement Axum API server and SvelteKit 5 dashboard with expanded SAST rules

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Implement Axum REST API server for web integration with /health and /api/v1/scan endpoints
• Build premium SvelteKit 5 dashboard with glassmorphism UI, dark mode, and interactive scan
  workbench
• Expand SAST engine with 6 regex-based vulnerability detection rules (SQL injection, XSS,
  cryptography, command injection)
• Add description field to findings and enhance secrets detection with proper test fixtures
Diagram
flowchart LR
  A["SAST Engine<br/>6 Regex Rules"] -->|findings| B["Axum API Server<br/>REST Endpoints"]
  C["Secrets Engine"] -->|findings| B
  B -->|JSON| D["SvelteKit 5 Dashboard<br/>Glassmorphism UI"]
  D -->|scan request| B
  E["AI Providers<br/>Anthropic/OpenAI"] -.->|config| B
Loading

Grey Divider

File Changes

1. crates/scanner/src/engines/sast.rs ✨ Enhancement +133/-10

Implement regex-based SAST vulnerability detection rules

crates/scanner/src/engines/sast.rs


2. crates/server/src/main.rs ✨ Enhancement +76/-0

Create Axum REST API server with scan endpoint

crates/server/src/main.rs


3. crates/scanner/src/finding.rs ✨ Enhancement +3/-1

Add description field to finding structures

crates/scanner/src/finding.rs


View more (15)
4. crates/scanner/src/engines/secrets.rs ✨ Enhancement +1/-0

Update secrets engine to include description field

crates/scanner/src/engines/secrets.rs


5. apps/web/src/routes/+page.svelte ✨ Enhancement +111/-12

Build dashboard homepage with stats and recent scans

apps/web/src/routes/+page.svelte


6. apps/web/src/routes/scan/+page.svelte ✨ Enhancement +198/-0

Create interactive scan workbench with code editor

apps/web/src/routes/scan/+page.svelte


7. apps/web/src/routes/+layout.svelte ✨ Enhancement +98/-4

Implement main layout with sidebar navigation and header

apps/web/src/routes/+layout.svelte


8. apps/web/src/app.css ✨ Enhancement +97/-4

Define design system tokens and glassmorphism components

apps/web/src/app.css


9. apps/web/vite.config.ts ⚙️ Configuration changes +10/-0

Configure Vite with SvelteKit and Tailwind CSS v4

apps/web/vite.config.ts


10. apps/web/src/routes/+layout.ts ⚙️ Configuration changes +2/-0

Disable SSR and prerendering for SvelteKit

apps/web/src/routes/+layout.ts


11. apps/web/src/app.html ✨ Enhancement +12/-0

Add HTML template for SvelteKit application

apps/web/src/app.html


12. crates/server/Cargo.toml Dependencies +19/-0

Define dependencies for Axum API server

crates/server/Cargo.toml


13. apps/web/package.json Dependencies +4/-3

Add Tailwind CSS v4 and vite plugin dependencies

apps/web/package.json


14. Cargo.toml ⚙️ Configuration changes +1/-0

Register server crate in workspace members

Cargo.toml


15. CHANGELOG.md 📝 Documentation +22/-0

Document Phase 2 MVP completion and feature additions

CHANGELOG.md


16. AGENTS.md 📝 Documentation +5/-2

Update project structure and status for Phase 2

AGENTS.md


17. apps/web/pnpm-lock.yaml Additional files +2009/-0

...

apps/web/pnpm-lock.yaml


18. apps/web/svelte.config.js Additional files +0/-1

...

apps/web/svelte.config.js


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Apr 4, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (3) 📎 Requirement gaps (0) 🎨 UX Issues (0)

Grey Divider


Action required

1. build_rules() uses .expect()📘 Rule violation ☼ Reliability
Description
Library SAST rule construction calls Regex::new(...).expect(...), which can panic at runtime
instead of returning an error. This violates the requirement to avoid unwrap()/expect() in
library/API code paths.
Code

crates/scanner/src/engines/sast.rs[R22-35]

+fn build_rules() -> Vec<SastRule> {
+    vec![
+        // ── Insecure Cryptography ──────────────────────────────────────────
+        SastRule {
+            name: "Insecure Hashing Algorithm (MD5)",
+            regex: Regex::new(r#"(?i)\bmd5\b\(|hashlib\.md5\(|crypto\.createHash\(['"]md5['"]\)"#).expect("valid regex"),
+            severity: Severity::Medium,
+            cwe_id: "CWE-327",
+            description: "MD5 is a cryptographically broken hashing algorithm. Use SHA-256 or Argon2 instead.",
+        },
+        SastRule {
+            name: "Insecure Hashing Algorithm (SHA1)",
+            regex: Regex::new(r#"(?i)\bsha1\b\(|hashlib\.sha1\(|crypto\.createHash\(['"]sha1['"]\)"#).expect("valid regex"),
+            severity: Severity::Low,
Evidence
PR Compliance ID 2 forbids .unwrap()/.expect() in library/API code; the new SAST engine
constructs regexes using .expect("valid regex"), which will panic on invalid patterns.

AGENTS.md
crates/scanner/src/engines/sast.rs[22-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`crates/scanner` library code uses `.expect()` when compiling regex rules, which can panic in production.
## Issue Context
`build_rules()` currently constructs `Regex` values via `Regex::new(...).expect("valid regex")`. Compliance requires explicit error handling instead of panic-based handling.
## Fix Focus Areas
- crates/scanner/src/engines/sast.rs[22-75]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. findings uses any[] 📘 Rule violation ⚙ Maintainability
Description
The new Svelte scan page introduces findings = $state([]), which weakens type safety and violates
strict TypeScript constraints. This can hide runtime shape mismatches from the scan API response.
Code

apps/web/src/routes/scan/+page.svelte[R15-17]

+  let isScanning = $state(false);
+  let findings = $state<any[]>([]);
+  let provider = $state("anthropic");
Evidence
PR Compliance ID 9 forbids introducing any; the added state declaration explicitly uses any[].

AGENTS.md
apps/web/src/routes/scan/+page.svelte[15-17]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The scan page uses `any[]` for `findings`, violating strict typing requirements.
## Issue Context
A typed `Finding` shape should be used (e.g., a local `type Finding = {...}` or an imported shared type from the API client layer).
## Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[15-18]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Direct fetch() in scan page 📘 Rule violation ⚙ Maintainability
Description
The scan UI calls fetch directly instead of using the typed API client module, bypassing
centralized typing and error handling. This violates the rule requiring all frontend fetch calls to
go through apps/web/src/lib/api.ts.
Code

apps/web/src/routes/scan/+page.svelte[R20-38]

+  const runScan = async () => {
+    isScanning = true;
+    findings = [];
+    
+    try {
+      const response = await fetch("http://localhost:8080/api/v1/scan", {
+        method: "POST",
+        headers: { "Content-Type": "application/json" },
+        body: JSON.stringify({
+          code,
+          language: "python",
+          engines: ["sast", "secrets"],
+          ai_config: {
+            provider,
+            api_key: "dummy-key", // In production this would come from secure storage or user session
+            model,
+          }
+        })
+      });
Evidence
PR Compliance ID 13 requires frontend network calls to use the typed API client; the new scan page
introduces a direct fetch("http://localhost:8080/api/v1/scan", ...) call.

AGENTS.md
apps/web/src/routes/scan/+page.svelte[20-38]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A direct `fetch()` call was added in a Svelte route, violating the requirement to centralize requests in `apps/web/src/lib/api.ts`.
## Issue Context
The scan request (POST `/api/v1/scan`) should be implemented as a typed function in `apps/web/src/lib/api.ts`, and the Svelte page should call that function.
## Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[20-42]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (6)
4. Stripe-like key in client code 📘 Rule violation ⛨ Security
Description
The scan page seeds the editor with a Stripe live-key-looking value (SK_LIVE_REDACTED_FOR_TESTING)
in client-side source. Even if intended as a dummy, it resembles a real secret format and violates
the no-secrets requirement for client/repo content.
Code

apps/web/src/routes/scan/+page.svelte[R4-13]

+  let code = $state(`// Paste your code here to scan for vulnerabilities
+def insecure_database_query(user_id):
+    # Potential SQL Injection
+    query = "SELECT * FROM users WHERE id = " + user_id
+    cursor.execute(query)
+    
+    # Potential hardcoded secret
+    stripe_key = "SK_LIVE_REDACTED_FOR_TESTING"
+    
+    return cursor.fetchone()`);
Evidence
PR Compliance ID 12 forbids secrets in client-side code/repo; the added default textarea content
contains a Stripe live-key-pattern string (SK_LIVE_...).

AGENTS.md
apps/web/src/routes/scan/+page.svelte[4-13]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Client-side source includes a secret-like token string (`SK_LIVE_...`) in the default code sample.
## Issue Context
Even if redacted, secret-shaped tokens should not be committed/shipped in frontend code. Replace with a clearly fake placeholder that does not match real provider formats.
## Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[4-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Severity case mismatch 🐞 Bug ≡ Correctness
Description
The scanner serializes Severity as snake_case (e.g., "high"), but the scan UI’s
getSeverityColor switch expects "High"/"Critical", so severity badges will consistently get the
default styling and display unexpected values.
Code

apps/web/src/routes/scan/+page.svelte[R50-57]

+  const getSeverityColor = (sev: string) => {
+    switch(sev) {
+      case "Critical": return "bg-red-500 text-white";
+      case "High": return "bg-orange-500 text-white";
+      case "Medium": return "bg-yellow-500 text-black";
+      case "Low": return "bg-blue-500 text-white";
+      default: return "bg-zinc-500 text-white";
+    }
Evidence
The backend explicitly serializes Severity using snake_case, while the frontend compares against
Title Case strings and uses that comparison to compute CSS classes and display values.

crates/scanner/src/finding.rs[89-98]
apps/web/src/routes/scan/+page.svelte[50-57]
apps/web/src/routes/scan/+page.svelte[145-152]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The API returns `severity` values like `"high"`/`"critical"` (snake_case), but the UI’s `getSeverityColor()` expects `"High"`/`"Critical"`, so the severity badge styling is wrong.
### Issue Context
- Rust uses `#[serde(rename_all = "snake_case")]` for `Severity`.
- The UI uses a string switch on `sev`.
### Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[50-57]
- crates/scanner/src/finding.rs[89-98]
### Suggested fix
Option A (frontend): normalize `sev` to lower-case and switch on `"critical" | "high" | ...`.
Option B (backend): change serialization to match the UI (e.g., remove `rename_all` or use a rename strategy that emits `"High"`/`"Critical"`), and keep it consistent across all clients.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Hardcoded localhost API URL 🐞 Bug ≡ Correctness
Description
The scan page calls http://localhost:8080/api/v1/scan, which will break scanning in any non-local
deployment and forces cross-origin requests unnecessarily.
Code

apps/web/src/routes/scan/+page.svelte[R25-38]

+      const response = await fetch("http://localhost:8080/api/v1/scan", {
+        method: "POST",
+        headers: { "Content-Type": "application/json" },
+        body: JSON.stringify({
+          code,
+          language: "python",
+          engines: ["sast", "secrets"],
+          ai_config: {
+            provider,
+            api_key: "dummy-key", // In production this would come from secure storage or user session
+            model,
+          }
+        })
+      });
Evidence
The frontend uses an absolute localhost URL; in production the API will not be reachable at
localhost from the user’s browser.

apps/web/src/routes/scan/+page.svelte[24-38]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The scan UI is hardcoded to `http://localhost:8080/api/v1/scan`, which breaks outside local dev.
### Issue Context
This prevents hosting the web UI and API behind the same domain/reverse proxy and forces CORS.
### Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[24-38]
### Suggested fix
- Use a relative URL (e.g. `fetch('/api/v1/scan', ...)`) when the web and API are served together.
- Or read the API base URL from a public env var (e.g. `PUBLIC_API_BASE_URL`) and build the URL from that.
- Consider using SvelteKit hooks/proxying during dev to avoid CORS entirely.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Overly permissive CORS🐞 Bug ⛨ Security
Description
The API enables CorsLayer::permissive() while binding to 0.0.0.0:8080, allowing any browser
origin to call the scan endpoint, which is risky because it accepts arbitrary code and optional AI
config.
Code

crates/server/src/main.rs[R28-35]

+    let app = Router::new()
+        .route("/health", get(health_check))
+        .route("/api/v1/scan", post(run_scan))
+        .layer(CorsLayer::permissive());
+
+    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
+    tracing::info!("Zenvra API listening on {}", listener.local_addr()?);
+    axum::serve(listener, app).await?;
Evidence
CorsLayer::permissive() allows all origins/methods/headers, and binding to 0.0.0.0 exposes the
service broadly; combined, any website can invoke the API from a victim’s browser (and consume
resources / interact with future auth/session mechanisms).

crates/server/src/main.rs[28-35]
crates/server/src/main.rs[11-17]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
CORS is fully permissive on an externally bound listener, making the scan endpoint callable from any browser origin.
### Issue Context
The endpoint accepts arbitrary code and may accept AI config (including API keys). Open CORS increases exposure and makes later cookie-based auth risky.
### Fix Focus Areas
- crates/server/src/main.rs[28-35]
### Suggested fix
- Replace `CorsLayer::permissive()` with a configured CORS layer:
- Allow only the known frontend origins (dev + prod) from env.
- Allow only required methods (GET for /health, POST for scan).
- Allow only required headers.
- Consider binding to `127.0.0.1` by default for local dev, and make `HOST`/`PORT` configurable for deployments.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Errors returned as success🐞 Bug ☼ Reliability
Description
When scanning fails, the API logs the error but still returns HTTP 200 with [], so clients cannot
distinguish a scan failure from a successful scan with no findings.
Code

crates/server/src/main.rs[R69-74]

+    match zenvra_scanner::scan(&config).await {
+        Ok(findings) => Json(findings),
+        Err(e) => {
+            tracing::error!("Scan failed: {}", e);
+            Json(vec![])
+        }
Evidence
The handler converts errors to an empty list without changing the HTTP status code or returning an
error payload.

crates/server/src/main.rs[69-74]
apps/web/src/routes/scan/+page.svelte[40-47]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The scan endpoint returns `200 OK` with an empty array on errors, hiding failures from the client.
### Issue Context
The frontend only checks `response.ok` and otherwise silently shows no results, making failures hard to debug.
### Fix Focus Areas
- crates/server/src/main.rs[69-74]
- apps/web/src/routes/scan/+page.svelte[40-47]
### Suggested fix
- Change the handler signature to return a `Result<...>` and use appropriate status codes:
- `400` for invalid requests (unknown engine, missing fields)
- `500` for internal scan errors
- Return a JSON error body (e.g., `{ "error": "..." }`) and have the UI display it.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Secrets re-exposed in output🐞 Bug ⛨ Security
Description
The secrets engine computes a redacted match but returns the full source line in vulnerable_code,
and the server returns it to the web UI, potentially leaking real credentials in API responses and
UI.
Code

crates/scanner/src/engines/secrets.rs[R186-192]

                severity: pattern.severity.clone(),
                title: format!("Hardcoded {} detected", pattern.name),
                vulnerable_code: line.to_string(),
+                    description: None,
                line_start: line_number,
                line_end: line_number,
                file_path: config.file_path.clone(),
Evidence
The secrets engine calculates redacted but populates vulnerable_code with the unredacted line;
the Finding type includes vulnerable_code, and the server returns Vec directly to clients.

crates/scanner/src/engines/secrets.rs[172-193]
crates/scanner/src/finding.rs[56-87]
crates/server/src/main.rs[46-74]
apps/web/src/routes/scan/+page.svelte[162-168]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The secrets engine returns the full matching line (likely containing the secret) in `vulnerable_code`, re-exposing credentials to API consumers and the UI.
### Issue Context
`redacted` is computed but not used for the returned finding, and the server returns findings directly.
### Fix Focus Areas
- crates/scanner/src/engines/secrets.rs[172-193]
- crates/scanner/src/finding.rs[56-87]
- crates/server/src/main.rs[46-74]
### Suggested fix
- Replace `vulnerable_code: line.to_string()` with a redacted representation (e.g., replace the matched substring with `redacted`).
- Optionally add separate fields (e.g., `redacted_code` or `secret_preview`) and never serialize the full secret.
- Add a test asserting no full secret value is present in serialized output.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

10. Dummy AI config always sent 🐞 Bug ☼ Reliability
Description
The scan UI always sends an ai_config (with a dummy API key), which triggers the scanner’s AI
enrichment path and will cause avoidable external calls/failures and extra latency on every scan.
Code

apps/web/src/routes/scan/+page.svelte[R28-37]

+        body: JSON.stringify({
+          code,
+          language: "python",
+          engines: ["sast", "secrets"],
+          ai_config: {
+            provider,
+            api_key: "dummy-key", // In production this would come from secure storage or user session
+            model,
+          }
+        })
Evidence
The UI always includes ai_config in the request; the scanner uses the presence of ai_config to
instantiate an AI provider and call explain()/generate_fix() for each finding.

apps/web/src/routes/scan/+page.svelte[28-37]
crates/scanner/src/lib.rs[51-72]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The UI always includes `ai_config`, which forces the backend to attempt AI enrichment every scan.
### Issue Context
With the current dummy key, AI calls will fail and generate warnings/empty fixes; even with a real key, this adds latency and sends sensitive keys from the browser.
### Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[28-37]
- crates/scanner/src/lib.rs[51-72]
### Suggested fix
- Only include `ai_config` when the user has explicitly enabled AI and provided a valid key.
- Otherwise omit `ai_config` (or send `null`) so the scanner returns fast, non-enriched findings.
- Consider moving key storage/handling server-side (session-bound) instead of sending it on every request.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Regexes rebuilt every scan🐞 Bug ➹ Performance
Description
The SAST engine compiles all regex rules on each scan (build_rules()), adding avoidable overhead
per request and scaling poorly under load.
Code

crates/scanner/src/engines/sast.rs[R21-76]

+/// Build the list of static analysis rules.
+fn build_rules() -> Vec<SastRule> {
+    vec![
+        // ── Insecure Cryptography ──────────────────────────────────────────
+        SastRule {
+            name: "Insecure Hashing Algorithm (MD5)",
+            regex: Regex::new(r#"(?i)\bmd5\b\(|hashlib\.md5\(|crypto\.createHash\(['"]md5['"]\)"#).expect("valid regex"),
+            severity: Severity::Medium,
+            cwe_id: "CWE-327",
+            description: "MD5 is a cryptographically broken hashing algorithm. Use SHA-256 or Argon2 instead.",
+        },
+        SastRule {
+            name: "Insecure Hashing Algorithm (SHA1)",
+            regex: Regex::new(r#"(?i)\bsha1\b\(|hashlib\.sha1\(|crypto\.createHash\(['"]sha1['"]\)"#).expect("valid regex"),
+            severity: Severity::Low,
+            cwe_id: "CWE-327",
+            description: "SHA-1 is no longer considered secure against well-funded attackers. Use SHA-256 or better.",
+        },
+        // ── Injection Vulnerabilities ──────────────────────────────────────
+        SastRule {
+            name: "Potential SQL Injection (String Concatenation)",
+            regex: Regex::new(r#"(?i)(SELECT|INSERT|UPDATE|DELETE|FROM).*(\+|=.*f['"]|\.format\(|\$\{).*"#).expect("valid regex"),
+            severity: Severity::High,
+            cwe_id: "CWE-89",
+            description: "Detected string concatenation in a SQL query. Use parameterized queries or an ORM to prevent SQL injection.",
+        },
+        SastRule {
+            name: "Potential OS Command Injection",
+            regex: Regex::new(r"(?i)\bos\.(system|popen)\(|subprocess\.(run|call|Popen)\(.*\bshell\s*=\s*True\b|child_process\.exec\(").expect("valid regex"),
+            severity: Severity::High,
+            cwe_id: "CWE-78",
+            description: "Executing OS commands with unsanitized input or via a shell can lead to command injection.",
+        },
+        // ── Cross-Site Scripting (XSS) ──────────────────────────────────────
+        SastRule {
+            name: "Insecure HTML Rendering (innerHTML)",
+            regex: Regex::new(r"(?i)\.innerHTML\s*=|dangerousSetInnerHTML|\$state\.raw\(.*<").expect("valid regex"),
+            severity: Severity::Medium,
+            cwe_id: "CWE-79",
+            description: "Directly setting innerHTML or using dangerouslySetInnerHTML bypasses sanitization and can lead to XSS.",
+        },
+        // ── Insecure Defaults ──────────────────────────────────────────────
+        SastRule {
+            name: "Hardcoded Localhost Reference",
+            regex: Regex::new(r"127\.0\.0\.1|localhost").expect("valid regex"),
+            severity: Severity::Info,
+            cwe_id: "CWE-1188",
+            description: "Hardcoded localhost references can cause issues when deploying to production.",
+        },
+    ]
+}
+
+/// Run the SAST scanner against the code in `config`.
+pub async fn run(config: &ScanConfig) -> Result<Vec<RawFinding>> {
+    let rules = build_rules();
+    let mut findings = Vec::new();
Evidence
build_rules() constructs Regex::new(...) for each rule and run() calls build_rules() per
scan, so regex compilation cost is paid every request.

crates/scanner/src/engines/sast.rs[21-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Regex compilation happens on every scan, increasing latency and CPU usage.
### Issue Context
Regex compilation is relatively expensive and rules are static.
### Fix Focus Areas
- crates/scanner/src/engines/sast.rs[21-76]
### Suggested fix
- Use `once_cell::sync::Lazy` (or `lazy_static`) to create a static `Vec<SastRule>` (or separate static regexes) compiled once.
- Reuse the same compiled rules across scans.
- Apply the same pattern to the secrets engine if needed (it also builds regexes per run).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +15 to +17
let isScanning = $state(false);
let findings = $state<any[]>([]);
let provider = $state("anthropic");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. findings uses any[] 📘 Rule violation ⚙ Maintainability

The new Svelte scan page introduces findings = $state<any[]>([]), which weakens type safety and
violates strict TypeScript constraints. This can hide runtime shape mismatches from the scan API
response.
Agent Prompt
## Issue description
The scan page uses `any[]` for `findings`, violating strict typing requirements.

## Issue Context
A typed `Finding` shape should be used (e.g., a local `type Finding = {...}` or an imported shared type from the API client layer).

## Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[15-18]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +20 to +38
const runScan = async () => {
isScanning = true;
findings = [];

try {
const response = await fetch("http://localhost:8080/api/v1/scan", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
code,
language: "python",
engines: ["sast", "secrets"],
ai_config: {
provider,
api_key: "dummy-key", // In production this would come from secure storage or user session
model,
}
})
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Direct fetch() in scan page 📘 Rule violation ⚙ Maintainability

The scan UI calls fetch directly instead of using the typed API client module, bypassing
centralized typing and error handling. This violates the rule requiring all frontend fetch calls to
go through apps/web/src/lib/api.ts.
Agent Prompt
## Issue description
A direct `fetch()` call was added in a Svelte route, violating the requirement to centralize requests in `apps/web/src/lib/api.ts`.

## Issue Context
The scan request (POST `/api/v1/scan`) should be implemented as a typed function in `apps/web/src/lib/api.ts`, and the Svelte page should call that function.

## Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[20-42]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +4 to +13
let code = $state(`// Paste your code here to scan for vulnerabilities
def insecure_database_query(user_id):
# Potential SQL Injection
query = "SELECT * FROM users WHERE id = " + user_id
cursor.execute(query)

# Potential hardcoded secret
stripe_key = "SK_LIVE_REDACTED_FOR_TESTING"

return cursor.fetchone()`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. Stripe-like key in client code 📘 Rule violation ⛨ Security

The scan page seeds the editor with a Stripe live-key-looking value (SK_LIVE_REDACTED_FOR_TESTING)
in client-side source. Even if intended as a dummy, it resembles a real secret format and violates
the no-secrets requirement for client/repo content.
Agent Prompt
## Issue description
Client-side source includes a secret-like token string (`SK_LIVE_...`) in the default code sample.

## Issue Context
Even if redacted, secret-shaped tokens should not be committed/shipped in frontend code. Replace with a clearly fake placeholder that does not match real provider formats.

## Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[4-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +50 to +57
const getSeverityColor = (sev: string) => {
switch(sev) {
case "Critical": return "bg-red-500 text-white";
case "High": return "bg-orange-500 text-white";
case "Medium": return "bg-yellow-500 text-black";
case "Low": return "bg-blue-500 text-white";
default: return "bg-zinc-500 text-white";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

5. Severity case mismatch 🐞 Bug ≡ Correctness

The scanner serializes Severity as snake_case (e.g., "high"), but the scan UI’s
getSeverityColor switch expects "High"/"Critical", so severity badges will consistently get the
default styling and display unexpected values.
Agent Prompt
### Issue description
The API returns `severity` values like `"high"`/`"critical"` (snake_case), but the UI’s `getSeverityColor()` expects `"High"`/`"Critical"`, so the severity badge styling is wrong.

### Issue Context
- Rust uses `#[serde(rename_all = "snake_case")]` for `Severity`.
- The UI uses a string switch on `sev`.

### Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[50-57]
- crates/scanner/src/finding.rs[89-98]

### Suggested fix
Option A (frontend): normalize `sev` to lower-case and switch on `"critical" | "high" | ...`.
Option B (backend): change serialization to match the UI (e.g., remove `rename_all` or use a rename strategy that emits `"High"`/`"Critical"`), and keep it consistent across all clients.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +25 to +38
const response = await fetch("http://localhost:8080/api/v1/scan", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
code,
language: "python",
engines: ["sast", "secrets"],
ai_config: {
provider,
api_key: "dummy-key", // In production this would come from secure storage or user session
model,
}
})
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

6. Hardcoded localhost api url 🐞 Bug ≡ Correctness

The scan page calls http://localhost:8080/api/v1/scan, which will break scanning in any non-local
deployment and forces cross-origin requests unnecessarily.
Agent Prompt
### Issue description
The scan UI is hardcoded to `http://localhost:8080/api/v1/scan`, which breaks outside local dev.

### Issue Context
This prevents hosting the web UI and API behind the same domain/reverse proxy and forces CORS.

### Fix Focus Areas
- apps/web/src/routes/scan/+page.svelte[24-38]

### Suggested fix
- Use a relative URL (e.g. `fetch('/api/v1/scan', ...)`) when the web and API are served together.
- Or read the API base URL from a public env var (e.g. `PUBLIC_API_BASE_URL`) and build the URL from that.
- Consider using SvelteKit hooks/proxying during dev to avoid CORS entirely.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Nitpick comments (3)
.gitignore (1)

9-13: Optional cleanup: remove stale .next ignore in SvelteKit section.

Since this section is now explicitly SvelteKit-focused, keeping .next is a bit confusing for future maintainers.

♻️ Suggested diff
 # Node / SvelteKit
 node_modules
 .svelte-kit
-.next
 out
 dist
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitignore around lines 9 - 13, Remove the stale `.next` entry from the
SvelteKit ignore block in .gitignore: locate the SvelteKit section containing
"node_modules", ".svelte-kit", ".next", and "out" and delete the ".next" line so
the file only lists SvelteKit-relevant ignores (keep "node_modules",
".svelte-kit", and "out"); if you still need Next.js ignores, move ".next" into
a separate Next.js-specific section instead.
apps/web/src/routes/+layout.ts (1)

1-2: Move ssr=false and prerender=false to apps/web/src/routes/scan/+layout.ts.

The root page is purely static content with no backend dependencies and could be SSR-rendered and prerendered. Only the scan page requires CSR due to direct fetch calls to http://localhost:8080/api/v1/scan. Currently, the root layout setting disables SSR/prerender for the entire app unnecessarily.

Create apps/web/src/routes/scan/+layout.ts with these flags and remove them from the root layout to allow the dashboard page to benefit from SSR and prerendering.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/`+layout.ts around lines 1 - 2, Move the CSR flags out of
the root layout: remove the exported ssr = false and prerender = false from the
root +layout.ts and add a new scan/+layout.ts that exports ssr = false and
prerender = false so only the scan route is client-side only; keep the root
+layout.ts free of those flags so the dashboard/root can be SSR/prerendered.
Ensure the new file exports the same symbols (ssr, prerender) and that any
imports/exports in the root +layout.ts are left unchanged aside from deleting
the two flag exports.
apps/web/src/routes/scan/+page.svelte (1)

16-16: Give findings a concrete type.

The template already depends on a stable response shape, so any[] just hides contract drift until runtime.

Possible direction
+  type ScanFinding = {
+    severity: string;
+    cwe_id: string | null;
+    title: string;
+    line_start: number;
+    description: string | null;
+    explanation: string;
+    vulnerable_code: string;
+    fixed_code: string;
+  };
-  let findings = $state<any[]>([]);
+  let findings = $state<ScanFinding[]>([]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/scan/`+page.svelte at line 16, Replace the use of any[]
for the findings store with a concrete interface that matches the template's
expected shape: declare an interface (e.g., Finding) containing the exact
properties the +page.svelte template reads (IDs, titles, statuses, timestamps,
metadata, etc.), then change the declaration from let findings =
$state<any[]>([]); to use the typed store let findings = $state<Finding[]>([]);
so the compiler enforces the contract (check the template references to
determine the exact fields to include).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/app.css`:
- Line 23: The four declarations that follow `@apply` directives (e.g., the
declaration starting with "font-feature-settings: 'cv02', 'cv03', 'cv04',
'cv11';" and the other three similar property declarations flagged) must be
preceded by a blank line to satisfy the Stylelint declaration-empty-line-before
rule; update each block so there is an empty line between the `@apply` line and
the subsequent declaration (i.e., insert one blank line before
"font-feature-settings: ..." and the other three offending declarations).
- Line 4: Update the Stylelint config so Tailwind v4 at-rules (like `@theme` in
apps/web/src/app.css) are allowed: edit .stylelintrc.json to either extend a
Tailwind-compatible config such as "stylelint-config-tailwindcss" or add a rule
override for "scss/at-rule-no-unknown" (or "at-rule-no-unknown") to ignore
Tailwind directives; ensure the change references the plugin/config name or the
rule key so CI no longer flags `@theme` as an unknown at-rule.

In `@apps/web/src/routes/`+layout.svelte:
- Around line 6-12: The navItems array in +layout.svelte references routes
("/history", "/settings/cve", "/settings/ai") that don't exist, causing 404s;
fix by either removing those entries from the navItems array or by adding Svelte
route components for each path (create corresponding +page.svelte files for
/history, /settings/cve, and /settings/ai with the intended UI and any load/data
functions), and ensure the route files export the expected page component and
any props used by layout; update navItems only if the new route components are
implemented and tested.

In `@apps/web/src/routes/`+page.svelte:
- Around line 44-45: The current class conditional on stat.trend only checks
stat.trend.startsWith('+') and treats everything else (including "0%") as
negative; update the conditional on the <span> that references stat.trend so it
explicitly handles three cases: positive when stat.trend.startsWith('+'),
negative when stat.trend.startsWith('-'), and a neutral fallback for any other
value (e.g., "0%") using an appropriate neutral class string; change the ternary
expression attached to stat.trend to a three-way conditional so the neutral case
gets its own styling instead of falling into the red branch.

In `@apps/web/src/routes/scan/`+page.svelte:
- Line 25: The fetch call hardcodes "http://localhost:8080/api/v1/scan"; change
it to use either a relative route or a configurable public env var. Replace the
literal URL in the fetch call with "/api/v1/scan" for same-origin requests, or
import a public env like PUBLIC_API_BASE from '$env/static/public' (import {
PUBLIC_API_BASE } from '$env/static/public') and call
fetch(`${PUBLIC_API_BASE}/api/v1/scan`) so deployments can configure the API
origin.
- Around line 40-45: The current fetch handling in +page.svelte treats non-2xx
responses as "no findings" because it only sets findings when response.ok;
update the fetch error handling inside the try/catch (the block that checks
response.ok and currently calls console.error on catch) so that non-ok responses
are handled explicitly: read the response body (response.json() or
response.text()), set a new error state (e.g., scanError) or populate findings
with an explicit error marker instead of leaving findings empty, and log the
status and body (replace or augment the existing console.error("Scan failed",
error)). Ensure you use the existing symbols findings and response.ok and
add/assign the new state (scanError or similar) so the UI can render an error
state for 4xx/5xx responses rather than showing "no findings."
- Around line 29-30: The scan payload currently hardcodes language: "python"
next to the code field, causing incorrect metadata; change the payload
construction to set the language dynamically (e.g., use the editor's current
language/mode variable or run a simple detectLanguage(code) helper) instead of
the literal "python", and add a sensible fallback like "text" so the object with
keys code and language uses the actual language value rather than the hardcoded
"python".
- Around line 69-81: Add accessible labels and restore visible focus styles for
the scan controls: wrap or pair a <label> with the provider <select>
(bind:value={provider}), the model <input> (bind:value={model>) and the code
<textarea> (the one referenced at lines 110-114), give each control a unique id
and set the label's for attribute accordingly, and remove the blanket
outline-none or replace it with an accessible focus utility (e.g., a
focus-visible:ring/focus-visible:outline or similar custom focus class) so
keyboard users see a visible focus indicator; ensure labels are screen-reader
friendly (visually-hidden class if you don't want visible text) and that the
select/input/textarea keep their existing styling but include the new focus
classes.

In `@crates/scanner/src/engines/sast.rs`:
- Around line 55-61: The SastRule entry named "Insecure HTML Rendering
(innerHTML)" currently matches `$state.raw(...)`, which is incorrect; update its
regex to detect the Svelte XSS sink `{`@html`}` instead. Edit the Regex::new(...)
pattern in the SastRule with name "Insecure HTML Rendering (innerHTML)" to
replace the `\$state\.raw\(.*<` alternative with a pattern that matches the
literal `{`@html`}` (allowing optional whitespace inside the braces and using the
existing case-insensitive flag), e.g. add `\{\s*@html\s*\}` as the alternative
so the rule matches `.innerHTML =`, `dangerousSetInnerHTML`, or `{`@html`}`.
- Around line 41-46: The SQLi rule "Potential SQL Injection (String
Concatenation)" uses the Regex::new(...) literal and currently only looks for
interpolation markers after the SQL keyword, so it misses Python f-strings like
f"SELECT ... {user_input}"; update the regex in that rule (the Regex::new call)
to also detect f-string patterns where the f- prefix appears before the opening
quote and where an interpolation brace ({) can appear anywhere in the string
(not only after the SQL keyword). Concretely, broaden the pattern to allow an
optional leading f/F before the string delimiter and to treat "{" or other
interpolation forms as a match even when they occur before the SQL keyword,
while preserving existing checks for +, .format, ${}, etc.

In `@crates/server/src/main.rs`:
- Around line 11-17: The ScanRequest currently uses plain strings for language
and engines which causes silent fallback to Language::Unknown and drops
unsupported engines; change ScanRequest.language to the concrete Language enum
and ScanRequest.engines to Vec<Engine> (use the Language and Engine types from
zenvra_scanner), ensure Serde is configured to fail on unknown variants (or add
a custom deserializer) so deserialization returns a 400 error instead of
defaulting, and update the request-handling code that previously called
Language::Unknown or filtered engines to instead validate/return errors when
unsupported values are provided so scans never silently degrade.
- Around line 46-75: The handler run_scan should not swallow
zenvra_scanner::scan errors and return 200 with an empty list; change the
function to return a Result<Json<Vec<Finding>>, impl IntoResponse> (e.g.,
Result<Json<Vec<Finding>>, (StatusCode, String)>) and map Err(e) to an
appropriate non-200 response such as (StatusCode::INTERNAL_SERVER_ERROR,
format!("Scan failed: {}", e)), keeping the tracing::error log; update imports
and the function signature accordingly so callers receive a proper HTTP error
status instead of an empty success payload.
- Line 31: The current use of CorsLayer::permissive() opens the API (including
/api/v1/scan) to all origins and must be replaced with an explicit origin
allowlist and auth middleware: replace CorsLayer::permissive() with a CorsLayer
built from allowed origins read from an environment variable (e.g., parse
ALLOWED_ORIGINS into Origin::list and use
CorsLayer::new().allow_origin(...).allow_methods(...).allow_headers(...)), and
add authentication middleware (a tower/axum auth layer or a middleware that
verifies bearer tokens/session cookies) that runs before handlers for
resource-heavy routes such as the /api/v1/scan handler; ensure the new CorsLayer
disallows credentials from untrusted origins and the auth layer rejects
unauthenticated requests, failing closed until SvelteKit frontend auth is
available.

---

Nitpick comments:
In @.gitignore:
- Around line 9-13: Remove the stale `.next` entry from the SvelteKit ignore
block in .gitignore: locate the SvelteKit section containing "node_modules",
".svelte-kit", ".next", and "out" and delete the ".next" line so the file only
lists SvelteKit-relevant ignores (keep "node_modules", ".svelte-kit", and
"out"); if you still need Next.js ignores, move ".next" into a separate
Next.js-specific section instead.

In `@apps/web/src/routes/`+layout.ts:
- Around line 1-2: Move the CSR flags out of the root layout: remove the
exported ssr = false and prerender = false from the root +layout.ts and add a
new scan/+layout.ts that exports ssr = false and prerender = false so only the
scan route is client-side only; keep the root +layout.ts free of those flags so
the dashboard/root can be SSR/prerendered. Ensure the new file exports the same
symbols (ssr, prerender) and that any imports/exports in the root +layout.ts are
left unchanged aside from deleting the two flag exports.

In `@apps/web/src/routes/scan/`+page.svelte:
- Line 16: Replace the use of any[] for the findings store with a concrete
interface that matches the template's expected shape: declare an interface
(e.g., Finding) containing the exact properties the +page.svelte template reads
(IDs, titles, statuses, timestamps, metadata, etc.), then change the declaration
from let findings = $state<any[]>([]); to use the typed store let findings =
$state<Finding[]>([]); so the compiler enforces the contract (check the template
references to determine the exact fields to include).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f6481d25-2307-42a9-87dd-fc869a5a67c0

📥 Commits

Reviewing files that changed from the base of the PR and between af083de and c63dc1a.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • apps/web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • .gitignore
  • AGENTS.md
  • CHANGELOG.md
  • Cargo.toml
  • apps/web/package.json
  • apps/web/src/app.css
  • apps/web/src/app.html
  • apps/web/src/routes/+layout.svelte
  • apps/web/src/routes/+layout.ts
  • apps/web/src/routes/+page.svelte
  • apps/web/src/routes/scan/+page.svelte
  • apps/web/svelte.config.js
  • apps/web/vite.config.ts
  • crates/scanner/src/engines/sast.rs
  • crates/scanner/src/engines/secrets.rs
  • crates/scanner/src/finding.rs
  • crates/server/Cargo.toml
  • crates/server/src/main.rs
💤 Files with no reviewable changes (1)
  • apps/web/svelte.config.js

@import "tailwindcss";

/* Zenvra — Design System Tokens */
@theme {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find stylelint configuration
find . -type f \( -name ".stylelintrc*" -o -name "stylelint.config.*" \) | head -20

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 96


🏁 Script executed:

# Check for stylelint config in package.json
cat package.json 2>/dev/null | jq '.stylelint' 2>/dev/null || grep -A 20 '"stylelint"' package.json 2>/dev/null

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 59


🏁 Script executed:

# Check Tailwind version
cat package.json 2>/dev/null | jq '.dependencies | with_entries(select(.key | contains("tailwind")))' 2>/dev/null || grep -E '"tailwindcss"' package.json 2>/dev/null

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 59


🏁 Script executed:

# Examine the actual app.css file
cat -n apps/web/src/app.css 2>/dev/null | head -30

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 1072


🏁 Script executed:

# Read stylelint config
cat .stylelintrc.json

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 1083


🏁 Script executed:

# Get full dependencies from package.json
cat package.json | jq '.dependencies, .devDependencies' 2>/dev/null || head -50 package.json

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 121


🏁 Script executed:

# Find package.json
find . -name "package.json" -type f

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 133


🏁 Script executed:

# Check Tailwind CSS version in any package.json found
find . -name "package.json" -type f -exec grep -l "tailwindcss" {} \;

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 100


🏁 Script executed:

# Get Tailwind version from web app package.json
cat apps/web/package.json | jq '.dependencies, .devDependencies' 2>/dev/null

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 373


Update Stylelint config to support Tailwind v4 directives.

The project uses Tailwind CSS v4 (tailwindcss@^4.0.0), which introduces the @theme at-rule used in apps/web/src/app.css. The current Stylelint configuration extends only stylelint-config-standard-scss and lacks Tailwind v4 support, causing it to flag @theme as an unknown at-rule. This will cause linting to fail in CI.

Add Tailwind v4 compatibility to .stylelintrc.json using either a dedicated plugin (e.g., stylelint-config-tailwindcss) or by disabling/configuring the scss/at-rule-no-unknown rule for Tailwind directives.

🧰 Tools
🪛 Stylelint (17.6.0)

[error] 4-4: Unexpected unknown at-rule "@theme" (scss/at-rule-no-unknown)

(scss/at-rule-no-unknown)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app.css` at line 4, Update the Stylelint config so Tailwind v4
at-rules (like `@theme` in apps/web/src/app.css) are allowed: edit
.stylelintrc.json to either extend a Tailwind-compatible config such as
"stylelint-config-tailwindcss" or add a rule override for
"scss/at-rule-no-unknown" (or "at-rule-no-unknown") to ignore Tailwind
directives; ensure the change references the plugin/config name or the rule key
so CI no longer flags `@theme` as an unknown at-rule.

@layer base {
body {
@apply bg-background text-zinc-100 antialiased;
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and read the relevant lines
if [ -f "apps/web/src/app.css" ]; then
  echo "=== File found, showing content with line numbers ==="
  cat -n "apps/web/src/app.css" | head -100
else
  echo "File not found at apps/web/src/app.css"
  # Search for app.css
  find . -name "app.css" -type f 2>/dev/null | head -5
fi

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 3557


🏁 Script executed:

# Check for Stylelint configuration files
echo "=== Looking for Stylelint config ==="
find . -maxdepth 3 -type f \( -name ".stylelintrc*" -o -name "stylelint.config.*" \) 2>/dev/null

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 133


🏁 Script executed:

# Check package.json for stylelint config
echo "=== Checking for stylelint in package.json ==="
if [ -f "package.json" ]; then
  jq '.stylelint' package.json 2>/dev/null || echo "No stylelint config in root package.json"
fi

if [ -f "apps/web/package.json" ]; then
  echo "=== apps/web/package.json ==="
  jq '.stylelint' apps/web/package.json 2>/dev/null || echo "No stylelint config in apps/web/package.json"
fi

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 158


🏁 Script executed:

cat .stylelintrc.json

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 1083


🏁 Script executed:

# Also check if there's any documentation or comments about the `@theme` issue
rg "@theme" apps/web/src/app.css -A 2 -B 2

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 224


Add blank lines before declarations following @apply directives.

The declaration-empty-line-before rule is active in Stylelint and these four declarations (lines 23, 57, 74, 90) violate it by appearing immediately after @apply directives without blank lines in between.

🧰 Tools
🪛 Stylelint (17.6.0)

[error] 23-23: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app.css` at line 23, The four declarations that follow `@apply`
directives (e.g., the declaration starting with "font-feature-settings: 'cv02',
'cv03', 'cv04', 'cv11';" and the other three similar property declarations
flagged) must be preceded by a blank line to satisfy the Stylelint
declaration-empty-line-before rule; update each block so there is an empty line
between the `@apply` line and the subsequent declaration (i.e., insert one blank
line before "font-feature-settings: ..." and the other three offending
declarations).

Comment on lines +11 to +17
#[derive(Debug, Serialize, Deserialize)]
struct ScanRequest {
code: String,
language: String,
engines: Vec<String>,
ai_config: Option<zenvra_scanner::ai::AiConfig>,
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use typed request fields for language and engines to avoid silent scan degradation.

Line 63 forces Language::Unknown, and Lines 52-58 silently discard unsupported engine values. This can return misleading “successful” scans with reduced coverage.

Suggested refactor
 #[derive(Debug, Serialize, Deserialize)]
 struct ScanRequest {
     code: String,
-    language: String,
-    engines: Vec<String>,
+    language: zenvra_scanner::Language,
+    engines: Vec<zenvra_scanner::Engine>,
     ai_config: Option<zenvra_scanner::ai::AiConfig>,
 }
@@
-    let engines = payload
-        .engines
-        .iter()
-        .filter_map(|e| match e.as_str() {
-            "sast" => Some(zenvra_scanner::Engine::Sast),
-            "sca" => Some(zenvra_scanner::Engine::Sca),
-            "secrets" => Some(zenvra_scanner::Engine::Secrets),
-            "ai_code" => Some(zenvra_scanner::Engine::AiCode),
-            _ => None,
-        })
-        .collect();
-
     let config = ScanConfig {
         code: payload.code,
-        language: zenvra_scanner::Language::Unknown, // TODO: Map language string
-        engines,
+        language: payload.language,
+        engines: payload.engines,
         ai_config: payload.ai_config,
         file_path: None,
     };

Also applies to: 49-67

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/server/src/main.rs` around lines 11 - 17, The ScanRequest currently
uses plain strings for language and engines which causes silent fallback to
Language::Unknown and drops unsupported engines; change ScanRequest.language to
the concrete Language enum and ScanRequest.engines to Vec<Engine> (use the
Language and Engine types from zenvra_scanner), ensure Serde is configured to
fail on unknown variants (or add a custom deserializer) so deserialization
returns a 400 error instead of defaulting, and update the request-handling code
that previously called Language::Unknown or filtered engines to instead
validate/return errors when unsupported values are provided so scans never
silently degrade.

let app = Router::new()
.route("/health", get(health_check))
.route("/api/v1/scan", post(run_scan))
.layer(CorsLayer::permissive());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify CORS posture and whether auth middleware exists nearby.
rg -n -C3 'CorsLayer::permissive|CorsLayer::new|allow_origin|allow_methods|allow_headers' --type rust
rg -n -C3 'Authorization|auth|jwt|bearer|middleware::from_fn|from_fn_with_state' --type rust

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 2883


🏁 Script executed:

cat -n crates/server/src/main.rs

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 2845


🏁 Script executed:

rg -n 'fn run_scan|fn health_check' --type rust -A 10

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 1260


🏁 Script executed:

# Check for any environment-based CORS config or feature flags
rg -n -i 'cors|origin|allow|config|env' crates/server/src/main.rs --type rust

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 1034


🏁 Script executed:

cat crates/server/Cargo.toml

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 627


🏁 Script executed:

# Check for any deployment docs or security guidance
find . -type f \( -name "README*" -o -name "SECURITY*" -o -name "*.md" \) | head -20 | xargs grep -l "cors\|auth\|api\|deploy" 2>/dev/null | head -5

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 186


🏁 Script executed:

# Check for feature flags or conditional compilation related to dev/prod
rg -n -i 'cfg\(|feature|dev|prod' crates/server/ --type rust

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 59


🏁 Script executed:

cat README.md | head -100

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 4032


🏁 Script executed:

# Check for any existing auth/api key patterns in project config or docs
rg -i 'api.?key|token|secret|auth' README.md AGENTS.md 2>/dev/null

Repository: Cameroon-Developer-Network/Zenvra

Length of output: 1849


Replace CorsLayer::permissive() with explicit origin allowlist and add authentication.

The /api/v1/scan endpoint accepts arbitrary code payloads without authentication and is exposed to all origins. This creates two risks: (1) unauthenticated users can trigger resource-intensive scans (DOS vector), and (2) credentials and auth tokens can be sent from any browser context. Use environment variables to configure trusted origins (or a single origin matching your SvelteKit frontend), and implement auth middleware before production deployment. Note: Auth is marked TBD in AGENTS.md for the SvelteKit frontend—ensure server-side protection is in place once frontend auth is live.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/server/src/main.rs` at line 31, The current use of
CorsLayer::permissive() opens the API (including /api/v1/scan) to all origins
and must be replaced with an explicit origin allowlist and auth middleware:
replace CorsLayer::permissive() with a CorsLayer built from allowed origins read
from an environment variable (e.g., parse ALLOWED_ORIGINS into Origin::list and
use CorsLayer::new().allow_origin(...).allow_methods(...).allow_headers(...)),
and add authentication middleware (a tower/axum auth layer or a middleware that
verifies bearer tokens/session cookies) that runs before handlers for
resource-heavy routes such as the /api/v1/scan handler; ensure the new CorsLayer
disallows credentials from untrusted origins and the auth layer rejects
unauthenticated requests, failing closed until SvelteKit frontend auth is
available.

…and TypeScript settings across web and extension projects.
@chojuninengu chojuninengu changed the title Feat/mvp foundation Feat mvp foundation for more features Apr 4, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
apps/web/src/routes/+layout.svelte (3)

17-19: Consider self-hosting fonts for production.

Loading fonts from Google Fonts CDN works but introduces external dependency latency and privacy implications (Google can track user page views). For an MVP this is acceptable, but consider downloading the fonts and serving them locally before production release.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/`+layout.svelte around lines 17 - 19, The page currently
loads Inter and Outfit from Google Fonts via the three <link> tags (hrefs to
fonts.googleapis.com and fonts.gstatic.com); to self-host for production,
download the needed font files (woff2 for the specified weights 400/500/600/700
for Inter and Outfit), place them into your static/assets/fonts folder, add
corresponding `@font-face` declarations (with font-display: swap and correct
weight/style) into your global CSS, and replace the external <link> tags with
local references (or remove them if CSS handles loading); ensure the static
assets are served by the app and update any crossorigin attributes and cache
headers for optimal delivery.

46-55: Hardcoded user data should be replaced with dynamic values.

The username "vibe coder" (line 64) and usage plan details (lines 48-53: "Free Tier", "4/10 scans remaining") are hardcoded. The +layout.ts file only exports SSR/prerender config without a load function to provide user session data.

For the MVP, consider either:

  1. Adding a load function to +layout.ts that fetches user/session data
  2. Adding a TODO comment to clarify these are intentional placeholders

Also applies to: 64-64

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/`+layout.svelte around lines 46 - 55, Replace the
hardcoded user and usage strings in +layout.svelte with dynamic props and supply
them from +layout.ts: add a load function in +layout.ts that fetches the current
user/session (e.g., user.name and user.plan/usage) and return those values so
+layout.svelte can reference them instead of "vibe coder", "Free Tier" and "4/10
scans remaining"; if you intentionally want placeholders for MVP, add a clear
TODO comment in +layout.svelte and/or +layout.ts explaining they are temporary
placeholders and must be replaced with real session data later.

90-105: Scrollbar styling is WebKit-only.

The ::-webkit-scrollbar pseudo-elements work in Chrome, Safari, and Edge, but Firefox users will see the default scrollbar. For consistent cross-browser styling, consider adding scrollbar-color and scrollbar-width CSS properties for Firefox:

♻️ Optional: Add Firefox scrollbar support
 <style>
+  :global(.custom-scrollbar) {
+    scrollbar-width: thin;
+    scrollbar-color: `#27272a` transparent;
+  }
   :global(.custom-scrollbar::-webkit-scrollbar) {
     width: 8px;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/routes/`+layout.svelte around lines 90 - 105, The scrollbar CSS
only targets WebKit; update the global styles for .custom-scrollbar to include
Firefox-compatible properties by adding scrollbar-width and scrollbar-color
declarations (e.g., scrollbar-width: thin; scrollbar-color: <thumb-color>
<track-color>) alongside the existing ::-webkit-scrollbar rules so Firefox users
get a similar appearance; modify the :global(.custom-scrollbar) rule in the
+layout.svelte style block to include these properties and adjust colors to
match the current `#27272a` thumb and transparent track.
extensions/vscode/package.json (1)

90-102: Consolidate typescript-eslint to a single major version.

eslint.config.js imports and uses typescript-eslint@8.58.0, which already provides v8 versions of @typescript-eslint/eslint-plugin and @typescript-eslint/parser. The v7 versions pinned in devDependencies are unused and create version drift. Remove them to consolidate to v8.

♻️ Proposed dependency cleanup
   "devDependencies": {
     "@eslint/js": "^10.0.1",
     "@types/node": "20.x",
     "@types/vscode": "^1.90.0",
-    "@typescript-eslint/eslint-plugin": "^7.0.0",
-    "@typescript-eslint/parser": "^7.0.0",
     "@vscode/test-electron": "^2.4.0",
     "@vscode/vsce": "^3.0.0",
     "eslint": "^9.0.0",
     "globals": "^17.4.0",
     "typescript": "^5.4.0",
     "typescript-eslint": "^8.58.0"
   },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extensions/vscode/package.json` around lines 90 - 102, package.json currently
lists "@typescript-eslint/eslint-plugin" and "@typescript-eslint/parser" pinned
to ^7.0.0 while eslint.config.js imports "typescript-eslint" v8
(typescript-eslint@8.58.0), causing version drift; remove the two v7
devDependencies ("@typescript-eslint/eslint-plugin" and
"@typescript-eslint/parser") from the devDependencies block in package.json so
the project uses the single consolidated "typescript-eslint" v8 package
(typescript-eslint 8.58.0) referenced by eslint.config.js, and run install to
update lockfile.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/src/routes/`+layout.svelte:
- Around line 17-19: The page currently loads Inter and Outfit from Google Fonts
via the three <link> tags (hrefs to fonts.googleapis.com and fonts.gstatic.com);
to self-host for production, download the needed font files (woff2 for the
specified weights 400/500/600/700 for Inter and Outfit), place them into your
static/assets/fonts folder, add corresponding `@font-face` declarations (with
font-display: swap and correct weight/style) into your global CSS, and replace
the external <link> tags with local references (or remove them if CSS handles
loading); ensure the static assets are served by the app and update any
crossorigin attributes and cache headers for optimal delivery.
- Around line 46-55: Replace the hardcoded user and usage strings in
+layout.svelte with dynamic props and supply them from +layout.ts: add a load
function in +layout.ts that fetches the current user/session (e.g., user.name
and user.plan/usage) and return those values so +layout.svelte can reference
them instead of "vibe coder", "Free Tier" and "4/10 scans remaining"; if you
intentionally want placeholders for MVP, add a clear TODO comment in
+layout.svelte and/or +layout.ts explaining they are temporary placeholders and
must be replaced with real session data later.
- Around line 90-105: The scrollbar CSS only targets WebKit; update the global
styles for .custom-scrollbar to include Firefox-compatible properties by adding
scrollbar-width and scrollbar-color declarations (e.g., scrollbar-width: thin;
scrollbar-color: <thumb-color> <track-color>) alongside the existing
::-webkit-scrollbar rules so Firefox users get a similar appearance; modify the
:global(.custom-scrollbar) rule in the +layout.svelte style block to include
these properties and adjust colors to match the current `#27272a` thumb and
transparent track.

In `@extensions/vscode/package.json`:
- Around line 90-102: package.json currently lists
"@typescript-eslint/eslint-plugin" and "@typescript-eslint/parser" pinned to
^7.0.0 while eslint.config.js imports "typescript-eslint" v8
(typescript-eslint@8.58.0), causing version drift; remove the two v7
devDependencies ("@typescript-eslint/eslint-plugin" and
"@typescript-eslint/parser") from the devDependencies block in package.json so
the project uses the single consolidated "typescript-eslint" v8 package
(typescript-eslint 8.58.0) referenced by eslint.config.js, and run install to
update lockfile.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2b8c8f08-0dff-4c4e-9213-5ed9c6bca062

📥 Commits

Reviewing files that changed from the base of the PR and between c63dc1a and dfc9e5f.

⛔ Files ignored due to path filters (2)
  • apps/web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • extensions/vscode/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • apps/web/eslint.config.js
  • apps/web/package.json
  • apps/web/src/lib/api.test.ts
  • apps/web/src/routes/+layout.svelte
  • apps/web/src/routes/+page.svelte
  • apps/web/src/routes/scan/+page.svelte
  • apps/web/tsconfig.json
  • crates/cli/src/main.rs
  • crates/scanner/src/ai/anthropic.rs
  • crates/scanner/src/ai/custom.rs
  • crates/scanner/src/ai/google.rs
  • crates/scanner/src/ai/openai.rs
  • crates/scanner/src/engine.rs
  • crates/scanner/src/engines/ai_code.rs
  • crates/scanner/src/engines/sast.rs
  • crates/scanner/src/engines/sca.rs
  • crates/scanner/src/engines/secrets.rs
  • extensions/vscode/eslint.config.js
  • extensions/vscode/package.json
✅ Files skipped from review due to trivial changes (11)
  • crates/scanner/src/ai/anthropic.rs
  • apps/web/src/lib/api.test.ts
  • crates/scanner/src/engines/sca.rs
  • crates/scanner/src/ai/google.rs
  • crates/scanner/src/engine.rs
  • crates/scanner/src/ai/custom.rs
  • crates/scanner/src/ai/openai.rs
  • crates/scanner/src/engines/ai_code.rs
  • apps/web/tsconfig.json
  • crates/cli/src/main.rs
  • apps/web/src/routes/+page.svelte
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/scanner/src/engines/secrets.rs
  • crates/scanner/src/engines/sast.rs
  • apps/web/src/routes/scan/+page.svelte
  • apps/web/package.json

@chojuninengu chojuninengu changed the title Feat mvp foundation for more features fix(ci): resolve failing workflows across rust, web, and vscode Apr 4, 2026
@chojuninengu chojuninengu self-assigned this Apr 4, 2026
@chojuninengu chojuninengu added the good first issue Good for newcomers label Apr 4, 2026
@chojuninengu chojuninengu removed their assignment Apr 4, 2026
@chojuninengu chojuninengu merged commit 1813a61 into main Apr 4, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

good first issue Good for newcomers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant