diff --git a/scenarios/06-rag-vector-classified-docs/README.md b/scenarios/06-rag-vector-classified-docs/README.md index db02c97..879a023 100755 --- a/scenarios/06-rag-vector-classified-docs/README.md +++ b/scenarios/06-rag-vector-classified-docs/README.md @@ -17,35 +17,32 @@ Before Oracle Deep Data Security, vector search can retrieve confidential HR, le ## Where To Run The Commands -Run commands from the repository root: - -```powershell -cd C:\Users\rodrigo\Documents\Codex\oracle-deep-data-security-lab -``` - -Connect to the database with SQLcl or SQL*Plus: +Run SQL scripts from the repository root. On Linux/macOS/WSL: ```bash -sql "" +cd ~/DEEP-DATA-SECURITY/oracle-deep-data-security-lab +export TNS_ADMIN=~/DEEP-DATA-SECURITY/wallet-ddslab +``` + +Connect as the lab administrator: + +```bash +sql admin@ddslab_tunnel ``` This scenario uses the `VECTOR` type, `TO_VECTOR`, and `VECTOR_DISTANCE`. Use a database version with Oracle AI Vector Search support. +SQLcl note: when running a script with `@file.sql`, press Enter once and wait for the output. Do not type `/` afterward, because `/` reruns the last command in the SQLcl buffer. + ## Step By Step - Before, Vulnerable Environment -1. Connect to the database: - - ```bash - sql "" - ``` - -2. Reset the scenario: +1. Reset the scenario as `ADMIN`: ```sql @scenarios/06-rag-vector-classified-docs/sql/99_reset.sql ``` -3. Create the chunk table, simple embeddings, and personas: +2. Create the chunk table, seed classified documents, and create personas: ```sql @scenarios/06-rag-vector-classified-docs/sql/00_schema.sql @@ -53,35 +50,53 @@ This scenario uses the `VECTOR` type, `TO_VECTOR`, and `VECTOR_DISTANCE`. Use a @scenarios/06-rag-vector-classified-docs/sql/02_identities.sql ``` +3. Show all available chunks as `ADMIN`: + + ```sql + SELECT chunk_id, document_title, department, classification, chunk_text + FROM dds_rag_chunks + ORDER BY chunk_id; + ``` + 4. Simulate the RAG question: ```text Summarize critical documents about renewals, people, and legal risks. ``` -5. Run the vector search: +5. Connect as `nina`, a regular employee: + + ```bash + sql 'nina/Welcome1_DDS!@ddslab_tunnel' + ``` + +6. Run the vector search before DDS: ```sql @scenarios/06-rag-vector-classified-docs/sql/04_test_queries.sql ``` -Expected result before protection: retrieval may return `HR_CONFIDENTIAL`, `LEGAL_CONFIDENTIAL`, and `EXECUTIVE_CONFIDENTIAL` chunks. +Expected result before protection: the retrieval may return `HR_CONFIDENTIAL`, `LEGAL_CONFIDENTIAL`, and `EXECUTIVE_CONFIDENTIAL` chunks to a regular employee because the legacy retrieval role is broad. ## Step By Step - After, With Deep Data Security -1. Apply data grants by classification: +1. Reconnect as `ADMIN` and apply data grants by classification: ```sql @scenarios/06-rag-vector-classified-docs/sql/03_data_grants.sql ``` -2. Run the same vector search again: +2. Connect as `nina` and run the same vector search: + + ```bash + sql 'nina/Welcome1_DDS!@ddslab_tunnel' + ``` ```sql @scenarios/06-rag-vector-classified-docs/sql/04_test_queries.sql ``` -3. Repeat the demo by simulating `nina`, `heitor`, `sofia`, and `carlos`. +3. Repeat the same test as `heitor`, `sofia`, and `carlos`. Expected result after protection: @@ -108,4 +123,3 @@ Linux/macOS: ## Demo Details See the complete walkthrough, evidence, and official references in [RUNBOOK.md](RUNBOOK.md). - diff --git a/scenarios/06-rag-vector-classified-docs/RUNBOOK.md b/scenarios/06-rag-vector-classified-docs/RUNBOOK.md index 259b379..33b128a 100755 --- a/scenarios/06-rag-vector-classified-docs/RUNBOOK.md +++ b/scenarios/06-rag-vector-classified-docs/RUNBOOK.md @@ -18,13 +18,27 @@ Show that a RAG agent retrieves only authorized chunks/documents before sending ## Before - Vulnerable Environment -1. Reset the scenario: +1. From the repository root, connect as `ADMIN`: + + ```bash + cd ~/DEEP-DATA-SECURITY/oracle-deep-data-security-lab + export TNS_ADMIN=~/DEEP-DATA-SECURITY/wallet-ddslab + sql admin@ddslab_tunnel + ``` + + Presenter note: `ADMIN` prepares the classified chunks and security personas. + + SQLcl note: after running a script with `@file.sql`, do not type `/`. The slash reruns the last command in the SQLcl buffer and can make a successful command look like an error. + +2. Reset the scenario: ```sql @scenarios/06-rag-vector-classified-docs/sql/99_reset.sql ``` -2. Create chunks and personas without applying data grants: + Presenter note: this removes prior Data Grants, roles, users, and test data. + +3. Create chunks and personas without applying data grants: ```sql @scenarios/06-rag-vector-classified-docs/sql/00_schema.sql @@ -32,18 +46,44 @@ Show that a RAG agent retrieves only authorized chunks/documents before sending @scenarios/06-rag-vector-classified-docs/sql/02_identities.sql ``` -3. Simulate the RAG question: + Presenter note: `rag_legacy_retrieval_role` simulates a broad RAG retrieval layer before DDS is enforced. + +4. Show every chunk and its classification: + + ```sql + SELECT chunk_id, document_title, department, classification, chunk_text + FROM dds_rag_chunks + ORDER BY chunk_id; + ``` + + Presenter note: explain that confidential chunks should not be sent to the LLM for every user. + +5. Simulate the RAG question: ```text Summarize critical documents about renewals, people, and legal risks. ``` -4. Run the vector search: +6. Exit and connect as Nina, a regular employee: + + ```sql + exit + ``` + + ```bash + sql 'nina/Welcome1_DDS!@ddslab_tunnel' + ``` + + Presenter note: Nina represents a regular employee using an internal copilot. + +7. Run the vector search before DDS: ```sql @scenarios/06-rag-vector-classified-docs/sql/04_test_queries.sql ``` + Presenter note: before DDS, a broad retrieval path can place HR, legal, or executive confidential chunks in the LLM context. + ## Expected Result Before - The search may retrieve `HR_CONFIDENTIAL`, `LEGAL_CONFIDENTIAL`, and `EXECUTIVE_CONFIDENTIAL` chunks. @@ -51,18 +91,50 @@ Show that a RAG agent retrieves only authorized chunks/documents before sending ## After - Applying Deep Data Security -1. Apply data grants by classification: +1. Exit and reconnect as `ADMIN`: + + ```sql + exit + ``` + + ```bash + sql admin@ddslab_tunnel + ``` + +2. Apply data grants by classification: ```sql @scenarios/06-rag-vector-classified-docs/sql/03_data_grants.sql ``` -2. Run the same search as `nina`, `heitor`, `sofia`, and `carlos`: + Presenter note: the database now filters chunks before the LLM receives any context. + +3. Test Nina after DDS: + + ```sql + exit + ``` + + ```bash + sql 'nina/Welcome1_DDS!@ddslab_tunnel' + ``` ```sql @scenarios/06-rag-vector-classified-docs/sql/04_test_queries.sql ``` + Presenter note: Nina should retrieve only `PUBLIC` and `INTERNAL` chunks. + +4. Repeat the same search as HR, legal, and executive personas: + + ```bash + sql 'heitor/Welcome1_DDS!@ddslab_tunnel' + sql 'sofia/Welcome1_DDS!@ddslab_tunnel' + sql 'carlos/Welcome1_DDS!@ddslab_tunnel' + ``` + + Presenter note: each persona receives only the chunk classifications authorized for that business role. + ## Expected Result After - `nina` retrieves only `PUBLIC` and `INTERNAL` chunks. @@ -83,4 +155,3 @@ Show that a RAG agent retrieves only authorized chunks/documents before sending - Create Data Grants: https://docs.oracle.com/en/database/oracle/oracle-database/26/ddscg/create-data-grants.html - TO_VECTOR SQL Reference: https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/to_vector.html - VECTOR operations in PL/SQL: https://docs.oracle.com/en/database/oracle/oracle-database/26/lnpls/sql-data-types.html - diff --git a/scenarios/06-rag-vector-classified-docs/evidence/expected-results.md b/scenarios/06-rag-vector-classified-docs/evidence/expected-results.md index dd75caa..3c5db77 100755 --- a/scenarios/06-rag-vector-classified-docs/evidence/expected-results.md +++ b/scenarios/06-rag-vector-classified-docs/evidence/expected-results.md @@ -1,8 +1,14 @@ # Expected Results +## Before Oracle Deep Data Security + +- A broad legacy retrieval role can return `HR_CONFIDENTIAL`, `LEGAL_CONFIDENTIAL`, and `EXECUTIVE_CONFIDENTIAL` chunks. +- A regular employee such as `nina` may receive sensitive chunks in the RAG context before the LLM generates an answer. + +## After Oracle Deep Data Security + - `nina` retrieves only `PUBLIC` and `INTERNAL` chunks. - `heitor` retrieves `HR_CONFIDENTIAL` plus public/internal chunks. - `sofia` retrieves `LEGAL_CONFIDENTIAL` plus public/internal chunks. -- `carlos` retrieves all classifications. +- `carlos` retrieves all classifications through the executive role. - The RAG layer receives only chunks authorized by the database policy. - diff --git a/scenarios/06-rag-vector-classified-docs/sql/00_schema.sql b/scenarios/06-rag-vector-classified-docs/sql/00_schema.sql index fe3b226..bfc0e99 100755 --- a/scenarios/06-rag-vector-classified-docs/sql/00_schema.sql +++ b/scenarios/06-rag-vector-classified-docs/sql/00_schema.sql @@ -1,11 +1,20 @@ WHENEVER SQLERROR EXIT SQL.SQLCODE -CREATE TABLE dds_rag_chunks ( - chunk_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - document_title VARCHAR2(160) NOT NULL, - department VARCHAR2(40) NOT NULL, - classification VARCHAR2(30) NOT NULL, - chunk_text VARCHAR2(1000) NOT NULL, - embedding VECTOR(3, FLOAT32) -); - +BEGIN + EXECUTE IMMEDIATE q'[ + CREATE TABLE dds_rag_chunks ( + chunk_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + document_title VARCHAR2(160) NOT NULL, + department VARCHAR2(40) NOT NULL, + classification VARCHAR2(30) NOT NULL, + chunk_text VARCHAR2(1000) NOT NULL, + embedding VECTOR(3, FLOAT32) + ) + ]'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -955 THEN + RAISE; + END IF; +END; +/ diff --git a/scenarios/06-rag-vector-classified-docs/sql/01_seed_data.sql b/scenarios/06-rag-vector-classified-docs/sql/01_seed_data.sql index 5008c46..37d784c 100755 --- a/scenarios/06-rag-vector-classified-docs/sql/01_seed_data.sql +++ b/scenarios/06-rag-vector-classified-docs/sql/01_seed_data.sql @@ -1,19 +1,22 @@ WHENEVER SQLERROR EXIT SQL.SQLCODE +SET DEFINE OFF -INSERT INTO dds_rag_chunks (document_title, department, classification, chunk_text, embedding) -VALUES ('Benefits Policy', 'HR', 'INTERNAL', 'General benefits policy available to employees.', TO_VECTOR('[0.10,0.20,0.30]')); +DELETE FROM dds_rag_chunks; -INSERT INTO dds_rag_chunks (document_title, department, classification, chunk_text, embedding) -VALUES ('Executive Compensation Plan', 'HR', 'HR_CONFIDENTIAL', 'Compensation calibration for executives and retention risks.', TO_VECTOR('[0.11,0.21,0.31]')); +INSERT INTO dds_rag_chunks (chunk_id, document_title, department, classification, chunk_text, embedding) +VALUES (1, 'Benefits Policy', 'HR', 'INTERNAL', 'General benefits policy available to employees.', TO_VECTOR('[0.10,0.20,0.30]')); -INSERT INTO dds_rag_chunks (document_title, department, classification, chunk_text, embedding) -VALUES ('Contract Renewal Risk', 'LEGAL', 'LEGAL_CONFIDENTIAL', 'Legal risk on renewal clauses for strategic accounts.', TO_VECTOR('[0.80,0.10,0.20]')); +INSERT INTO dds_rag_chunks (chunk_id, document_title, department, classification, chunk_text, embedding) +VALUES (2, 'Executive Compensation Plan', 'HR', 'HR_CONFIDENTIAL', 'Compensation calibration for executives and retention risks.', TO_VECTOR('[0.11,0.21,0.31]')); -INSERT INTO dds_rag_chunks (document_title, department, classification, chunk_text, embedding) -VALUES ('Company Travel Guide', 'GENERAL', 'PUBLIC', 'Public travel and expense guidance for all employees.', TO_VECTOR('[0.20,0.70,0.10]')); +INSERT INTO dds_rag_chunks (chunk_id, document_title, department, classification, chunk_text, embedding) +VALUES (3, 'Contract Renewal Risk', 'LEGAL', 'LEGAL_CONFIDENTIAL', 'Legal risk on renewal clauses for strategic accounts.', TO_VECTOR('[0.80,0.10,0.20]')); -INSERT INTO dds_rag_chunks (document_title, department, classification, chunk_text, embedding) -VALUES ('Board M&A Briefing', 'EXEC', 'EXECUTIVE_CONFIDENTIAL', 'Potential acquisition targets and board-level financial exposure.', TO_VECTOR('[0.90,0.20,0.40]')); +INSERT INTO dds_rag_chunks (chunk_id, document_title, department, classification, chunk_text, embedding) +VALUES (4, 'Company Travel Guide', 'GENERAL', 'PUBLIC', 'Public travel and expense guidance for all employees.', TO_VECTOR('[0.20,0.70,0.10]')); + +INSERT INTO dds_rag_chunks (chunk_id, document_title, department, classification, chunk_text, embedding) +VALUES (5, 'Board M&A Briefing', 'EXEC', 'EXECUTIVE_CONFIDENTIAL', 'Potential acquisition targets and board-level financial exposure.', TO_VECTOR('[0.90,0.20,0.40]')); COMMIT; - +SET DEFINE ON diff --git a/scenarios/06-rag-vector-classified-docs/sql/02_identities.sql b/scenarios/06-rag-vector-classified-docs/sql/02_identities.sql index 7fd9e46..b80a631 100755 --- a/scenarios/06-rag-vector-classified-docs/sql/02_identities.sql +++ b/scenarios/06-rag-vector-classified-docs/sql/02_identities.sql @@ -17,6 +17,15 @@ GRANT rag_session_role TO rag_hr_role; GRANT rag_session_role TO rag_legal_role; GRANT rag_session_role TO rag_exec_role; +-- Vulnerable baseline: this broad role simulates a RAG retrieval layer that can +-- query every chunk before DDS is enforced. +CREATE ROLE rag_legacy_retrieval_role; +GRANT SELECT ON dds_rag_chunks TO rag_legacy_retrieval_role; +GRANT rag_legacy_retrieval_role TO rag_employee_role; +GRANT rag_legacy_retrieval_role TO rag_hr_role; +GRANT rag_legacy_retrieval_role TO rag_legal_role; +GRANT rag_legacy_retrieval_role TO rag_exec_role; + GRANT DATA ROLE rag_employee_role TO nina; GRANT DATA ROLE rag_hr_role TO heitor; GRANT DATA ROLE rag_legal_role TO sofia; diff --git a/scenarios/06-rag-vector-classified-docs/sql/99_reset.sql b/scenarios/06-rag-vector-classified-docs/sql/99_reset.sql index 38d619b..9e8626d 100755 --- a/scenarios/06-rag-vector-classified-docs/sql/99_reset.sql +++ b/scenarios/06-rag-vector-classified-docs/sql/99_reset.sql @@ -20,6 +20,8 @@ BEGIN EXECUTE IMMEDIATE 'DROP DATA ROLE rag_exec_role'; EXCEPTION WHEN OTHERS TH / BEGIN EXECUTE IMMEDIATE 'DROP ROLE rag_session_role'; EXCEPTION WHEN OTHERS THEN NULL; END; / +BEGIN EXECUTE IMMEDIATE 'DROP ROLE rag_legacy_retrieval_role'; EXCEPTION WHEN OTHERS THEN NULL; END; +/ BEGIN EXECUTE IMMEDIATE 'DROP END USER nina'; EXCEPTION WHEN OTHERS THEN NULL; END; / BEGIN EXECUTE IMMEDIATE 'DROP END USER heitor'; EXCEPTION WHEN OTHERS THEN NULL; END;