Code Review Inbox

Read-only snapshot across 4 repo(s) · generated 2026-09-15T21:32:22.952Z · low-risk PRs with no critical finding are already auto-reviewed and auto-merged, nothing to do there

Repos (4)
RepoNeeds reviewMerge pendingAuto-mergedTotal tracked
qualogy-solutions/jira-ticket-analysis 1 0 1 3
qualogy-solutions/qualogy-scraper 1 0 0 1
qualogy-solutions/qualogy-websites 1 2 0 3
qualogy-solutions/sales-insights-dashboard 0 0 0 0

🔺 Needs your review — across all repos (3)

Fix guardrail dangerous_instructions finding suppression in orchestrator
qualogy-solutions/jira-ticket-analysis · #6 by Deborah · feature/QRAD-267 → main · Open in Bitbucket →
high

This PR fixes a security-relevant bug in the orchestrator where dangerous_instructions guardrail findings were computed but silently discarded, causing potentially harmful tickets to bypass human approval. It changes core authorization/guardrail merge logic that gates whether human review is required, affecting the safety-critical approval pipeline for all ticket processing.

  • Touches core/orchestrator.py's guardrail evaluation and resolve_execution_authorization call path, which determines whether human approval is bypassed
  • Changes security-sensitive fail-closed behavior (ADR-6 part 5) around a classifier used to detect dangerous/harmful instructions
  • Previously silent finding-suppression bug being fixed here means behavior change could newly trigger (or fail to trigger) human approval gates across all tenants
  • Modifies shared data models (ContentGuardrailFinding -> GuardrailFinding) and merges findings into a unified guardrail_result used by multiple call sites
  • Mistakes here could either cause false negatives (dangerous content slipping through) or over-blocking (excessive human approval gates), both high impact for a safety/security control

This is a well-reasoned bugfix: it properly merges the dangerous_instructions classifier findings into the same guardrail_result instead of discarding them, and replaces the "None guardrail_result" fail-closed hack with an explicit failed_checks entry that is now surfaced in checks_run/authorization_reason. Tests are updated accordingly and add a new regression test for the previously-silent finding-suppression bug. No correctness, security, or edge-case issues found in the diff.

No inline comments.

Feature/aanvraag mail module
qualogy-solutions/qualogy-scraper · #1 by Deborah · feature/aanvraag-mail-module → main · Open in Bitbucket →
high

This PR is a massive, sprawling change (147 files, +8120/-91933 lines) that overhauls the entire application architecture: replacing the old main.py dashboard server with a new FastAPI app, adding a new mailer module that sends live emails to Salesforce, rewriting Docker deployment config (Dockerfile, docker-compose, non-root user, healthcheck, port change from 8765 to 8000), deleting the committed .env with live production credentials, and modifying every scraper module's shared driver/setup code. A regression in the mailer, scheduler, or Docker/deploy config could break scraper execution, mis-deliver or leak sales lead emails, or take down the deployed service entirely.

  • Very large, sprawling diff touching 147 files across nearly every module in the repo simultaneously
  • New mailer module (app/mailer.py) sends real emails to a business-critical Salesforce mailbox — a bug could spam, leak, or drop sales leads
  • Infrastructure/deployment changes: Dockerfile rewritten (non-root user, new CMD/entrypoint, port 8765->8000, healthcheck), docker-compose.yml modified
  • Secrets handling changed: committed .env with live credentials deleted and replaced by .env.example — risk of breaking deployed configs if not migrated correctly
  • Architectural replacement of the main runner/dashboard (main.py) with a new FastAPI app (app/main.py, routers, runner, scheduler) — significant behavior change to a running service
  • Shared refactor of scraper base code (common/scraper_base.py) used by all scraper modules (Magnit, Mercell, Bergler, APG Magnit, etc.), so a bug affects multiple independent scraping pipelines at once
  • Diff includes binary/output artifacts and downloaded customer documents committed to the repo, indicating messy scope and potential data handling concerns

Large PR bundling a big architectural refactor (Flask/CLI dashboard → FastAPI app, new common/ shared scraper helpers, mail module, tests) together with committing/removing hundreds of unrelated binary artifacts (scraped output, downloaded .docx/.xlsx/.pdf files, .pyc files, node_modules-style n8n workflow json). Given the diff truncation I could only review a subset in detail (Dockerfile, .env handling, scrapers refactor, README). No blocking correctness bugs found in what was reviewed, but there are real repo-hygiene and security concerns that should be addressed before merge.

.env:1 · critical
This diff deletes the checked-in .env file, but the fact that a .env containing real plaintext credentials (MERCELL_PASSWORD, MAGNIT_PASSWORD, APG_MAGNIT_PASSWORD, STRIIVE_PASSWORD, FLEXTENDER_PASSWORD, STEDIN_PASSWORD, STAFFINGNOW_PASSWORD, BERGLER_PASSWORD, and an n8n webhook URL) was ever committed to the repository means these secrets are permanently present in git history and must be treated as compromised. Rotate every one of these credentials immediately regardless of merging this PR, and consider scrubbing history (BFG/git-filter-repo) if the repo can be rewritten.
Dockerfile:45 · warning
The image now runs as non-root `appuser`, which is good, but `RUN python -c "...ChromeDriverManager().install()"` happens before `USER appuser` is set while `WDM_LOCAL=1`/`ENV` are already set — this caches the driver under `/app/.wdm` while still running as root during build, then `chown -R appuser:appuser /app` afterwards. Verify the ordering actually gives appuser write access to `/app/.wdm` at runtime (e.g. if scrapers ever try to re-download to that path) and that no other root-owned files remain under /app after the chown that the app needs to write to besides `output/`.
Mercell/Mercell Scraper.py:587 · warning
wait_for_downloads_to_finish only considers files already present in `before` as pre-existing; if two tender downloads race (e.g. leftover slow download from a previous tender iteration completes during the current tender's wait window) a document could be misattributed to the wrong tender_id since attribution is purely based on directory diff timing, not filename/content correlation. Given tenders are processed sequentially with a driver.get() reload in between this is a low-probability edge case, but worth a comment/guard given documents are moved (not copied) into tender-specific folders based on this diff alone.
APG Magnit/APG Magnit Scraper.py:66 · info
Env var name for the max-per-run setting was changed from APG_MAGNIT_MAX_OPDRACHTEN_PER_RUN to APG_MAGNIT_MAX_AANVRAGEN_PER_RUN to match .env.example, which is fine, but double check any external deployment configs (Azure app settings, CI secrets) that reference the old variable name are updated too, otherwise the limit silently reverts to unlimited (default 0) in production.
QRAD-51: Security & performance audit hardening
qualogy-solutions/qualogy-websites · #24 by Prijanka Ramcharan · QRAD-51 → main · Open in Bitbucket →
high

This PR adds a new Content-Security-Policy and other security headers globally via next.config.ts, and changes the rate-limiter API contract (limiter.check now returns an object with .success instead of a boolean) across all public-facing form/API routes (contact, apply, meeting, quote, coffee-call, ATS). If the CSP is too strict or misconfigured, it can silently break scripts, styles, images, or third-party integrations (GTM, Cookiebot, Turnstile, Supabase, Vercel Analytics) site-wide. If the rate-limiter contract change is inconsistent anywhere, requests could be incorrectly blocked or rate limiting could silently no-op, affecting all public lead-gen/application endpoints.

  • Adds global security headers (CSP, HSTS, X-Frame-Options, Permissions-Policy) in next.config.ts affecting every route/page site-wide
  • Changes the rate-limiter's public contract (boolean -> {success}) and updates call sites across 6+ public API routes (contact, apply/ats, apply/hardcoded, meeting, coffee-call, quote) - security-relevant logic touching abuse/spam protection
  • CSP misconfiguration is a classic source of silent breakage for scripts/analytics/embeds (GTM, Cookiebot, Turnstile, YouTube, Supabase) that is easy to miss in review/testing
  • Adds new third-party dependency (@vercel/speed-insights) and lockfile changes across the monorepo
  • Touches multiple independent public-facing API routes simultaneously, increasing scope of potential regression
  • New logging utility (logEvent) replacing console.error across routes - low risk itself but part of a broad, cross-cutting change
  • Large number of files changed (27) spanning config, API routes, new package module, and binary asset updates, increasing chance of an overlooked interaction

Overall a solid, low-risk hardening PR: adds structured logging via a shared logger, CSP/security headers, and Vercel Speed Insights. One notable inconsistency in apply/ats/route.ts where the rate limiter API usage differs from all sibling routes — worth confirming createRateLimiter's actual return type before merge. A few other minor points below.

apps/qualogy-web/app/api/apply/ats/route.ts:12 · warning
This route now calls `await limiter.check(ip)` and checks `.success`, while every other route in this PR (contact, meeting, coffee-call, ai-oplossingen, ai-trainingen, apply/hardcoded) still uses `if (!limiter.check(ip))` (presumably synchronous boolean). Confirm which is the actual current signature of `createRateLimiter().check` — if it's synchronous returning a boolean, this route will incorrectly await a boolean and access `.success` on it (always undefined, so `!rateLimit.success` is always true, blocking every request with a 429). If the API was changed to return a Promise<{success}> only here, the other six routes are now broken instead. This needs to be verified/fixed for correctness.
apps/qualogy-web/next.config.ts:37 · info
`script-src` includes `'unsafe-inline'`, which significantly weakens the CSP's XSS protection (allows inline <script> execution). This is likely needed for GTM/Cookiebot inline snippets, but consider using nonces or hashes for the known inline scripts (e.g. the GTM snippet in layout.tsx) instead of blanket 'unsafe-inline' if feasible in a future iteration.
packages/security/src/logger.ts:16 · info
logEvent logs error messages/meta as-is; callers pass `meta: { error: String(error) } }` which could include stack traces or potentially sensitive data (e.g., request payload details) if errors are constructed with such info. Worth double-checking that no PII/secrets end up in these log lines, since Vercel logs may be retained/searchable.