Skip to content

Add RAG sensitive data exposure - #19

Merged
preetkaran20 merged 14 commits into
SasanLabs:mainfrom
luks-santos:rag-sensitive-data-exposure
Jul 20, 2026
Merged

Add RAG sensitive data exposure#19
preetkaran20 merged 14 commits into
SasanLabs:mainfrom
luks-santos:rag-sensitive-data-exposure

Conversation

@luks-santos

@luks-santos luks-santos commented May 17, 2026

Copy link
Copy Markdown
Contributor

Overview

Adds a RAG Sensitive Data Exposure lab (OWASP LLM02) built around Acme Corp's internal wiki assistant. Four levels tell a chronological story: each one adds a defense, and the next shows why it isn't enough — until the hardened level fixes the root cause.

Levels

  1. L1 — No defense. Direct retrieval hands the secret straight back.
  2. L2 — Lexical denylist. Blocks a few words, but semantic search still matches a paraphrase.
  3. L3 — Document-level tagging. A sensitivity: low tag on the whole document misses a sensitive chunk buried inside it.
  4. L4 — Hardened. Chunks are scanned and reclassified at ingest time, so the sensitive chunk is filtered out and nothing leaks (Variant.SECURE, no secret to capture).

How it works

RagDataExposureVectorStore combines FAISS (per-namespace index) with SQLite (chunk text + metadata), using Ollama for embeddings and chat. The facade shows retrieved chunks, offers progressive hints instead of a pre-filled winning prompt, and validates the captured secret with a constant-time comparison. Secrets are fixed but opaque values, obtainable only through retrieval.

Closes #8

Summary by CodeRabbit

  • New Features
    • Added a RAG Sensitive Data Exposure vulnerability lab with four progressive levels.
    • Added retrieval testing, sensitive-data detection, secret verification, and level-specific attack scenarios.
    • Added a dedicated interface for running queries, reviewing retrieved documents, and verifying secrets.
    • Added support for a secure, non-capturable final level.
  • Bug Fixes
    • Improved input validation and clearer handling of unsupported actions and unavailable services.
  • Tests
    • Added automated coverage for validation, sensitivity detection, retrieval behavior, and endpoint registration.

@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c76f0fc-0dee-493e-8a51-b19746553b0d

📥 Commits

Reviewing files that changed from the base of the PR and between 79bc9b8 and 9c3a409.

📒 Files selected for processing (3)
  • src/service/vulnerabilities/rag_data_exposure_lab.py
  • tests/conftest.py
  • tests/test_rag_data_exposure_registry.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/test_rag_data_exposure_registry.py
  • tests/conftest.py

📝 Walkthrough

Walkthrough

A new four-level RAG Sensitive Data Exposure lab adds FAISS/SQLite retrieval, sensitivity scanning, secret validation, FastAPI endpoints, document corpora, an interactive frontend, tests, and local Compose support.

Changes

RAG Sensitive Data Exposure Lab

Layer / File(s) Summary
Framework registration and exports
src/framework/*, src/service/vulnerabilities/__init__.py, src/app.py, locale/messages_us.properties
Registers the vulnerability type, controller, registry validation path, public service exports, and localized attack/payload strings.
RAG storage and evaluation pipeline
src/service/vulnerabilities/rag_data_exposure_lab.py
Implements sensitivity scanning, four-level configuration, FAISS/SQLite storage, indexing, filtered retrieval, LLM evaluation, response assembly, and capturable versus hardened secret validation.
Level-specific document corpora
src/service/vulnerabilities/docs/RAG_DATA_EXPOSURE/LEVEL*/documents.json
Adds document and chunk datasets for levels 1–4, including sensitivity metadata and embedded test values.
API endpoints and frontend facade
src/controllers/rag_data_exposure_controller.py, src/static/facade/rag_data_exposure_template.*
Adds generate/validate endpoints for four levels and an HTML/CSS/JavaScript interface for retrieval, verification, document rendering, feedback, and responsive layout.
Tests and runtime support
tests/*, pytest.ini, requirements-dev.txt, docker-compose.yml, .gitignore, .dockerignore
Adds lab and registry coverage, isolated test storage, pytest configuration, generated-data ignores, and Compose support including Mailpit and service environment settings.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant Controller
  participant Lab as RAG Lab
  participant Store as FAISS SQLite Store
  participant Embedder
  participant LLM

  Browser->>Controller: POST generate request
  Controller->>Lab: evaluate level and input
  Lab->>Store: ensure index and retrieve matches
  Store->>Embedder: embed documents and query
  Embedder->>Store: return vectors
  Store->>Lab: return filtered documents
  Lab->>LLM: send prompt with retrieved context
  LLM->>Lab: return assistant output
  Lab->>Controller: return evaluation response
  Controller->>Browser: return output and retrieved documents
Loading

Possibly related issues

  • RAG Vulnerability #8: Addresses the RAG vulnerability area, while this change implements a distinct sensitive-data-exposure lab with four levels, retrieval mechanics, and hardened secret handling.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.97% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main addition: a RAG sensitive data exposure lab.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@luks-santos luks-santos changed the title Add RAG sensitive data exposure L1 Add RAG sensitive data exposure May 17, 2026
Lexical denylist (password, secret, admin) bypassed by semantic
paraphrase retrieval.

- Add L2 corpus and denylist input handler
- Add level2 endpoint and locale entries
- Make the facade level-aware
@luks-santos
luks-santos force-pushed the rag-sensitive-data-exposure branch from d3b5626 to 5ef3d01 Compare May 24, 2026 23:14
@luks-santos
luks-santos marked this pull request as ready for review May 24, 2026 23:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/controllers/rag_data_exposure_controller.py`:
- Around line 47-53: The controller currently treats any non-"validate" action
as "generate"; add an explicit allowlist check for action values ("validate" or
"generate") and reject others with an error response. In the handler around the
action variable, validate that action is one of {"validate","generate"} and
return a 4xx error (or raise the appropriate HTTPException) for invalid values;
keep existing calls to validate_rag_data_exposure_secret when action ==
"validate" and to evaluate_rag_data_exposure_level when action == "generate"
(use the same user_input/model extraction). Ensure you reference the action
variable, validate_rag_data_exposure_secret, and
evaluate_rag_data_exposure_level when implementing this guard so bad clients
fail fast instead of following the wrong path.
- Around line 42-44: In the _handle_level method move the JSON parsing (await
request.json()) inside the existing try/except so malformed JSON can't bubble
out; specifically, call await request.json() within the try block at the start
of _handle_level, validate/normalize action = str(data.get("action",
"generate")).strip().lower() there, and in the except handle
JSONDecodeError/ValueError by returning a controlled error dict (and/or
appropriate status payload) instead of letting an unhandled 500 occur.

In `@src/framework/registry.py`:
- Around line 99-100: The branch comparing controller_name uses a hyphenated
string ("rag-sensitive-data-exposure") but other dispatches expect underscored
names (e.g., "rag_sensitive_data_exposure"), causing mismatches; update the
dispatch logic in the function that handles controller_name (the branch that
calls validate_rag_data_exposure_secret) to normalize controller_name (e.g.,
controller_name = controller_name.replace('-', '_') or compare against both
forms) before the if checks so the compare will match and
validate_rag_data_exposure_secret(level_number, candidate_secret) is reached for
registrations named rag_sensitive_data_exposure.

In `@src/service/vulnerabilities/rag_data_exposure_lab.py`:
- Around line 195-231: The current flow appends vectors to FAISS before
inserting rows into SQLite which can cause FAISS/DB divergence on
unique-constraint failures; change the order so you build the DB rows and
perform the INSERT (using self._connect() and handling
sqlite3.IntegrityError/unique-constraint races by skipping or deduping duplicate
rows) before mutating FAISS, then compute start_vector_id = int(index.ntotal),
call faiss.normalize_L2(matrix) and index.add(matrix); apply the same
swap-and-tolerate-duplicate-insert logic to the other identical block referenced
around lines 461-471 (use symbols: index.add, faiss.normalize_L2,
start_vector_id, self._connect, rows, documents).

In `@src/static/facade/rag_data_exposure_template.html`:
- Around line 11-21: The textarea (id="ragExposurePrompt") and the secret input
lack programmatic labels; add accessible labels by either inserting <label
for="ragExposurePrompt">Prompt</label> tied to the textarea and a corresponding
<label for="..."> for the secret input, or by adding clear aria-label attributes
to those elements, and if needed use a visually-hidden utility class to keep
visual layout unchanged; ensure the labels reference the exact element ids used
in this template so screen readers can announce the fields.

In `@src/static/facade/rag_data_exposure_template.js`:
- Around line 72-78: The UI currently accepts any integer level >=1 (from the
level parsing code) but endpointForLevel only implements level1..level3, causing
404s for higher levels; clamp the parsed level to the supported range (1..3) or
clamp inside endpointForLevel so that any incoming level is reduced to
Math.min(Math.max(level, 1), 3) before building the URL. Update the level
parsing function or endpointForLevel (referencing endpointForLevel and the
level-parsing code that sets const level = Number(match[1])) to enforce this
clamp so requests always target /level1, /level2, or /level3.
🪄 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 Plus

Run ID: 14462f52-df10-4037-8223-dc7e7b71afe4

📥 Commits

Reviewing files that changed from the base of the PR and between eb06cf7 and 5ef3d01.

📒 Files selected for processing (13)
  • locale/messages_us.properties
  • src/app.py
  • src/controllers/rag_data_exposure_controller.py
  • src/framework/decorators.py
  • src/framework/registry.py
  • src/service/vulnerabilities/__init__.py
  • src/service/vulnerabilities/docs/RAG_DATA_EXPOSURE/LEVEL1/documents.json
  • src/service/vulnerabilities/docs/RAG_DATA_EXPOSURE/LEVEL2/documents.json
  • src/service/vulnerabilities/docs/RAG_DATA_EXPOSURE/LEVEL3/documents.json
  • src/service/vulnerabilities/rag_data_exposure_lab.py
  • src/static/facade/rag_data_exposure_template.css
  • src/static/facade/rag_data_exposure_template.html
  • src/static/facade/rag_data_exposure_template.js

Comment thread src/controllers/rag_data_exposure_controller.py Outdated
Comment thread src/controllers/rag_data_exposure_controller.py
Comment thread src/framework/registry.py Outdated
Comment thread src/service/vulnerabilities/rag_data_exposure_lab.py
Comment thread src/static/facade/rag_data_exposure_template.html Outdated
Comment thread src/static/facade/rag_data_exposure_template.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/controllers/rag_data_exposure_controller.py`:
- Around line 44-47: The handler assumes request.json() returns a dict and calls
data.get(), which will raise if JSON root is a list/string; update the code
around data = await request.json() to validate that data is a dict (e.g.,
isinstance(data, dict)) before using data.get("action", ...), and if it isn't,
return a structured error (e.g., {"error": "Invalid JSON root type: expected
object"}) so the existing action handling (action variable and the action not in
{"generate","validate"} check) only runs on valid input.
🪄 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 Plus

Run ID: 10128d38-5302-4a02-9134-8cdfeb79cb2d

📥 Commits

Reviewing files that changed from the base of the PR and between 5ef3d01 and 10c6261.

📒 Files selected for processing (5)
  • src/controllers/rag_data_exposure_controller.py
  • src/framework/registry.py
  • src/service/vulnerabilities/rag_data_exposure_lab.py
  • src/static/facade/rag_data_exposure_template.html
  • src/static/facade/rag_data_exposure_template.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/static/facade/rag_data_exposure_template.html
  • src/framework/registry.py
  • src/static/facade/rag_data_exposure_template.js
  • src/service/vulnerabilities/rag_data_exposure_lab.py

Comment thread src/controllers/rag_data_exposure_controller.py
@preetkaran20

Copy link
Copy Markdown
Member

I tried to run this PR and following are my observations which I think should be corrected:

  1. The default payloads always fetch the secrets. I think payloads that can help fetch secret should be part of hints and let user try and find out the secrets, instead of a one-click "Use" button that solves it.

  2. Secrets should be random and should not be guessable (e.g. rag_l2_secret literally names the level + the answer). Generate them per level/session.

  3. I am unable to find the usecase, there should be some theme of what is happening. One consistent story across levels would help. Like say Acme Corp runs an internal-wiki RAG assistant for identity/support staff. Acme initially believed that they dont need any security and passed everything to LLM (Level 1) but later added security control by deny list like you added but that also had issues and then in level 3, they decided sensitive tags but someone in leadership added wrong sensitive flag or say updated low sensitive flag with a sensitive secret and that also caused issues (Level 3)

  4. Also please add a secure level to show the correct implementation — chunk-level sensitivity (via automated scanning at ingestion, not a human-typed doc tag), so parse the chunks and see if they contains any secrets, PII etc and just change the sensitivity.

@preetkaran20

Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@preetkaran20 preetkaran20 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you for the PR. I really liked the idea. Added a few comments.

@preetkaran20

Copy link
Copy Markdown
Member

@luks-santos pinging again in case notification was missed.

@preetkaran20

Copy link
Copy Markdown
Member

I tried to run this PR and following are my observations which I think should be corrected:

  1. The default payloads always fetch the secrets. I think payloads that can help fetch secret should be part of hints and let user try and find out the secrets, instead of a one-click "Use" button that solves it.
  2. Secrets should be random and should not be guessable (e.g. rag_l2_secret literally names the level + the answer). Generate them per level/session.
  3. I am unable to find the usecase, there should be some theme of what is happening. One consistent story across levels would help. Like say Acme Corp runs an internal-wiki RAG assistant for identity/support staff. Acme initially believed that they dont need any security and passed everything to LLM (Level 1) but later added security control by deny list like you added but that also had issues and then in level 3, they decided sensitive tags but someone in leadership added wrong sensitive flag or say updated low sensitive flag with a sensitive secret and that also caused issues (Level 3)
  4. Also please add a secure level to show the correct implementation — chunk-level sensitivity (via automated scanning at ingestion, not a human-typed doc tag), so parse the chunks and see if they contains any secrets, PII etc and just change the sensitivity.

@luks-santos

@luks-santos

Copy link
Copy Markdown
Contributor Author

@preetkaran20 Thanks for the detailed feedback! These are great suggestions. I'll implement the suggested changes

@luks-santos

Copy link
Copy Markdown
Contributor Author

I'll try to work on this over the weekend. Thanks!

@luks-santos

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review, it was really helpful. I'm actively working through all of the feedback, including restructuring the Acme Corp narrative and the other suggested improvements. I'll push the changes soon for another review. Thanks again!

@luks-santos
luks-santos marked this pull request as draft July 12, 2026 22:44
luks-santos and others added 4 commits July 12, 2026 22:18
…ts, progressive hints, hardened L4

Addresses review feedback on the RAG Sensitive Data Exposure lab:
- Replace guessable rag_lN_secret values with opaque, non-descriptive secrets
- Rewrite L1-L3 corpora around a consistent Acme Corp narrative (no security
  controls -> keyword denylist -> mistagged document sensitivity)
- Remove one-click auto-solve: blank input no longer runs the winning prompt,
  and the facade now offers progressive hints instead of a pre-filled payload
- Add a hardened Level 4 that scans each chunk at ingest and reclassifies
  sensitivity automatically, demonstrating the correct chunk-level control
  versus the human-typed document tag that caused the L3 bypass
- Add a pytest harness with unit coverage for secret handling, input
  validation, the ingest-time scanner, and endpoint registration

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ata exposure lab

Replace the custom hint/solution buttons with tiered @attack_vector
hints (narrative context -> nudge -> real payload) surfaced through
the existing VulnerabilityDefinitions mechanism, matching the pattern
used by prompt_injection and CachePoisoning. Also give each level a
story title ("Level 1 - The Break-Glass Leak") and a fixed Acme Corp
premise in the facade header.
The llmforge service pointed at ../llmforge as its build context,
which only works if a sibling directory happens to be named that way;
fix it to use the repo root (.) so a plain clone works. Also picks up
the mailpit service and ENABLE_VULNERABLEAPP_* facade flags already
present on origin/main, which this branch predates. Ignore the
FAISS/SQLite data the RAG Sensitive Data Exposure lab generates at
runtime so it never lands in git or the Docker build context.
Each level now shows a one-sentence, spoiler-free story beat in the
header explaining what Acme changed at that point (denylist, sensitivity
tags, ingest scanner) without needing to click through a hint. Since
that context previously lived only in the first framework hint tier,
remove it there to avoid repeating the same sentence twice: L1-L3 go
from 3 hint tiers to 2 (nudge, then payload), L4 from 2 to 1
(verification guidance only).
@luks-santos

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review! Here's what I changed to address the feedback:

  1. Consistent narrative. All four levels now follow one story, Acme Corp's internal-wiki RAG assistant, showing how its security evolved and broke at each step:
    • L1: No security.
    • L2: A denylist bypassed semantically.
    • L3: Document-level tags missing a mistagged chunk.
    • L4: An ingest-time scanner that fixes the issue.

Each level's UI now shows a chapter title and a short, always-visible summary, so the overall story is clear without needing to dig into the hints.

  1. Hints instead of a one-click solve. The "Reveal solution" button has been removed. Hints now use the framework's native attack_vector mechanism (the same one used by prompt_injection and rag_context_poisoning) and provide a short progressive chain:

    • A technique nudge.
    • Then a concrete payload/query.
  2. Non-guessable secrets. Each level now has its own opaque, level-scoped secret instead of anything predictable.

  3. A real secure level (L4). L4 reuses L3's leaky corpus but scans every chunk during ingest and reclassifies anything sensitive as high sensitivity, ensuring it is filtered out during retrieval regardless of the document's human-applied tag.

Let me know if you'd like any adjustments before another pass.

@luks-santos
luks-santos marked this pull request as ready for review July 18, 2026 22:00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
.dockerignore (1)

12-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Sync ignored paths with .gitignore.

To ensure locally generated vector store databases aren't accidentally copied into the Docker image build context (which could cause stale container state or inflate image size), consider adding data/rag_data_exposure/ here so it matches your .gitignore.

💡 Proposed fix
 data/*.sqlite3
 data/faiss.index
 data/faiss-docstore.json
+data/rag_data_exposure/
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.dockerignore around lines 12 - 14, Add data/rag_data_exposure/ to
.dockerignore alongside the existing data exclusions so Docker build contexts
match the repository’s ignored paths and omit locally generated vector-store
data.
src/service/vulnerabilities/rag_data_exposure_lab.py (1)

424-427: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dynamically list valid levels in the error message.

Hardcoding the level numbers in the error message will require manual updates if new levels are added. Consider using the keys of the LEVELS dictionary to dynamically generate the message.

♻️ Proposed refactor
 def _challenge_for(level: int) -> RagDataExposureLevel:
     if level not in LEVELS:
-        raise ValueError("level must be 1, 2, 3 or 4")
+        valid_levels = ", ".join(str(k) for k in sorted(LEVELS.keys()))
+        raise ValueError(f"level must be one of: {valid_levels}")
     return LEVELS[level]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/service/vulnerabilities/rag_data_exposure_lab.py` around lines 424 - 427,
Update _challenge_for so its ValueError message derives the valid level values
from LEVELS.keys() instead of hardcoding “1, 2, 3 or 4”; preserve the existing
validation and LEVELS lookup behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/service/vulnerabilities/rag_data_exposure_lab.py`:
- Around line 663-664: Update the secret comparison in the challenge validation
flow to UTF-8 encode both the stripped candidate value and
challenge.secret_token before passing them to hmac.compare_digest. Preserve
false-result behavior for invalid or non-ASCII input instead of allowing a
TypeError to escape.

---

Nitpick comments:
In @.dockerignore:
- Around line 12-14: Add data/rag_data_exposure/ to .dockerignore alongside the
existing data exclusions so Docker build contexts match the repository’s ignored
paths and omit locally generated vector-store data.

In `@src/service/vulnerabilities/rag_data_exposure_lab.py`:
- Around line 424-427: Update _challenge_for so its ValueError message derives
the valid level values from LEVELS.keys() instead of hardcoding “1, 2, 3 or 4”;
preserve the existing validation and LEVELS lookup behavior.
🪄 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 Plus

Run ID: ced83e06-76a2-4c25-9fd8-97126a8f7799

📥 Commits

Reviewing files that changed from the base of the PR and between 9b33f5f and 79bc9b8.

📒 Files selected for processing (18)
  • .dockerignore
  • .gitignore
  • docker-compose.yml
  • locale/messages_us.properties
  • pytest.ini
  • requirements-dev.txt
  • src/controllers/rag_data_exposure_controller.py
  • src/service/vulnerabilities/docs/RAG_DATA_EXPOSURE/LEVEL1/documents.json
  • src/service/vulnerabilities/docs/RAG_DATA_EXPOSURE/LEVEL2/documents.json
  • src/service/vulnerabilities/docs/RAG_DATA_EXPOSURE/LEVEL3/documents.json
  • src/service/vulnerabilities/docs/RAG_DATA_EXPOSURE/LEVEL4/documents.json
  • src/service/vulnerabilities/rag_data_exposure_lab.py
  • src/static/facade/rag_data_exposure_template.css
  • src/static/facade/rag_data_exposure_template.html
  • src/static/facade/rag_data_exposure_template.js
  • tests/conftest.py
  • tests/test_rag_data_exposure_lab.py
  • tests/test_rag_data_exposure_registry.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/service/vulnerabilities/docs/RAG_DATA_EXPOSURE/LEVEL2/documents.json
  • src/static/facade/rag_data_exposure_template.css
  • src/service/vulnerabilities/docs/RAG_DATA_EXPOSURE/LEVEL1/documents.json

Comment thread src/service/vulnerabilities/rag_data_exposure_lab.py Outdated
@luks-santos
luks-santos requested a review from preetkaran20 July 18, 2026 22:19
Comment thread src/service/vulnerabilities/rag_data_exposure_lab.py Outdated
"was logged and never redacted. Document-level tags are too coarse - the bypass "
"is to ask about content that lives inside the misclassified low-sensitivity doc."
),
metadata_filter=L3_METADATA_FILTER,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think adding deny list to Level 3 and Level 4 is really important. We need defence in depth at all the layers so security of deny list at application layer is good for level 3 and level 4.

Comment thread tests/conftest.py Outdated

@preetkaran20 preetkaran20 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@luks-santos this is a great great great contribution. just a small feedback.

  1. I think when something is marked as sensitive high, we should not return that document to the UI. That way we have made secure level as secure.
  2. Adding deny list to level 3 and level 4

@luks-santos

Copy link
Copy Markdown
Contributor Author

@preetkaran20
Thanks for the feedback. I'm glad you liked it!

Regarding the comments in Portuguese, they actually slipped past me while I was reviewing the code, haha. I've updated them to English now.

As for the other points, the sensitivity-high filtering is actually already how L4 works today: once a chunk gets reclassified, it's excluded from the search results before it ever reaches the retrieved-documents panel, so it never shows up in the UI. The denylist suggestion for L3 and L4 is a good one though, I'll add that.

I'm working on these improvements throughout the week and will push them soon.

@preetkaran20

preetkaran20 commented Jul 20, 2026

Copy link
Copy Markdown
Member

@preetkaran20 Thanks for the feedback. I'm glad you liked it!

Regarding the comments in Portuguese, they actually slipped past me while I was reviewing the code, haha. I've updated them to English now.

As for the other points, the sensitivity-high filtering is actually already how L4 works today: once a chunk gets reclassified, it's excluded from the search results before it ever reaches the retrieved-documents panel, so it never shows up in the UI. The denylist suggestion for L3 and L4 is a good one though, I'll add that.

I'm working on these improvements throughout the week and will push them soon.

@luks-santos I think it is good to merge it now and have deny list added as a separate PR.

@preetkaran20
preetkaran20 merged commit 62c756f into SasanLabs:main Jul 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RAG Vulnerability

2 participants