fix(ci): resolve failing workflows across rust, web, and vscode#2
fix(ci): resolve failing workflows across rust, web, and vscode#2chojuninengu merged 8 commits intomainfrom
Conversation
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (11)
📝 WalkthroughWalkthroughAdds 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
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
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly Related PRs
Suggested Reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Review Summary by QodoImplement Axum API server and SvelteKit 5 dashboard with expanded SAST rules
WalkthroughsDescription• 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 Diagramflowchart 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
File Changes1. crates/scanner/src/engines/sast.rs
|
Code Review by Qodo
1.
|
| let isScanning = $state(false); | ||
| let findings = $state<any[]>([]); | ||
| let provider = $state("anthropic"); |
There was a problem hiding this comment.
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
| 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, | ||
| } | ||
| }) | ||
| }); |
There was a problem hiding this comment.
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
| 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()`); |
There was a problem hiding this comment.
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
| 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"; | ||
| } |
There was a problem hiding this comment.
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
| 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, | ||
| } | ||
| }) | ||
| }); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (3)
.gitignore (1)
9-13: Optional cleanup: remove stale.nextignore in SvelteKit section.Since this section is now explicitly SvelteKit-focused, keeping
.nextis 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: Movessr=falseandprerender=falsetoapps/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.tswith 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: Givefindingsa 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
⛔ Files ignored due to path filters (2)
Cargo.lockis excluded by!**/*.lockapps/web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (18)
.gitignoreAGENTS.mdCHANGELOG.mdCargo.tomlapps/web/package.jsonapps/web/src/app.cssapps/web/src/app.htmlapps/web/src/routes/+layout.svelteapps/web/src/routes/+layout.tsapps/web/src/routes/+page.svelteapps/web/src/routes/scan/+page.svelteapps/web/svelte.config.jsapps/web/vite.config.tscrates/scanner/src/engines/sast.rscrates/scanner/src/engines/secrets.rscrates/scanner/src/finding.rscrates/server/Cargo.tomlcrates/server/src/main.rs
💤 Files with no reviewable changes (1)
- apps/web/svelte.config.js
| @import "tailwindcss"; | ||
|
|
||
| /* Zenvra — Design System Tokens */ | ||
| @theme { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find stylelint configuration
find . -type f \( -name ".stylelintrc*" -o -name "stylelint.config.*" \) | head -20Repository: 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/nullRepository: 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/nullRepository: 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 -30Repository: Cameroon-Developer-Network/Zenvra
Length of output: 1072
🏁 Script executed:
# Read stylelint config
cat .stylelintrc.jsonRepository: 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.jsonRepository: Cameroon-Developer-Network/Zenvra
Length of output: 121
🏁 Script executed:
# Find package.json
find . -name "package.json" -type fRepository: 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/nullRepository: 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"; |
There was a problem hiding this comment.
🧩 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
fiRepository: 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/nullRepository: 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"
fiRepository: Cameroon-Developer-Network/Zenvra
Length of output: 158
🏁 Script executed:
cat .stylelintrc.jsonRepository: 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 2Repository: 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).
| #[derive(Debug, Serialize, Deserialize)] | ||
| struct ScanRequest { | ||
| code: String, | ||
| language: String, | ||
| engines: Vec<String>, | ||
| ai_config: Option<zenvra_scanner::ai::AiConfig>, | ||
| } |
There was a problem hiding this comment.
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.
crates/server/src/main.rs
Outdated
| let app = Router::new() | ||
| .route("/health", get(health_check)) | ||
| .route("/api/v1/scan", post(run_scan)) | ||
| .layer(CorsLayer::permissive()); |
There was a problem hiding this comment.
🧩 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 rustRepository: Cameroon-Developer-Network/Zenvra
Length of output: 2883
🏁 Script executed:
cat -n crates/server/src/main.rsRepository: Cameroon-Developer-Network/Zenvra
Length of output: 2845
🏁 Script executed:
rg -n 'fn run_scan|fn health_check' --type rust -A 10Repository: 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 rustRepository: Cameroon-Developer-Network/Zenvra
Length of output: 1034
🏁 Script executed:
cat crates/server/Cargo.tomlRepository: 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 -5Repository: 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 rustRepository: Cameroon-Developer-Network/Zenvra
Length of output: 59
🏁 Script executed:
cat README.md | head -100Repository: 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/nullRepository: 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.
There was a problem hiding this comment.
🧹 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.tsfile only exports SSR/prerender config without aloadfunction to provide user session data.For the MVP, consider either:
- Adding a
loadfunction to+layout.tsthat fetches user/session data- 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-scrollbarpseudo-elements work in Chrome, Safari, and Edge, but Firefox users will see the default scrollbar. For consistent cross-browser styling, consider addingscrollbar-colorandscrollbar-widthCSS 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: Consolidatetypescript-eslintto a single major version.
eslint.config.jsimports and usestypescript-eslint@8.58.0, which already provides v8 versions of@typescript-eslint/eslint-pluginand@typescript-eslint/parser. The v7 versions pinned indevDependenciesare 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
⛔ Files ignored due to path filters (2)
apps/web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlextensions/vscode/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (19)
apps/web/eslint.config.jsapps/web/package.jsonapps/web/src/lib/api.test.tsapps/web/src/routes/+layout.svelteapps/web/src/routes/+page.svelteapps/web/src/routes/scan/+page.svelteapps/web/tsconfig.jsoncrates/cli/src/main.rscrates/scanner/src/ai/anthropic.rscrates/scanner/src/ai/custom.rscrates/scanner/src/ai/google.rscrates/scanner/src/ai/openai.rscrates/scanner/src/engine.rscrates/scanner/src/engines/ai_code.rscrates/scanner/src/engines/sast.rscrates/scanner/src/engines/sca.rscrates/scanner/src/engines/secrets.rsextensions/vscode/eslint.config.jsextensions/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
Summary by CodeRabbit
New Features
Documentation
Style
Chores