Wire DDS variant into run.sh as optional subcommands

Adds dds / dds-setup / dds-tests / dds-teardown subcommands so the
26ai Deep Data Security variant can be run from the same one-click
entry point. Not part of `./run.sh all` since DDS requires 26ai
(23.26.2+) which not every ADB has.

- sql/adb/14_tests_dds_user.sql: shared verification script for all
  4 ddsuser_*; uses WHENEVER SQLERROR CONTINUE so the expected
  ORA-00942 (deny-by-hiding) doesn't abort the script. Includes
  bypass attempts against the underlying VPD views, raw DB Links,
  and the VPD permission tables.
- sql/adb/15_dds_cleanup.sql: idempotent teardown for DDS objects
  (data grants, end users, data roles, dds_db_role, DDS-only views).
- run.sh: do_dds_prereq / do_dds_setup / do_dds_tests /
  do_dds_teardown helpers; dispatch case extended.

Also fixes a pre-existing secrets-leak gap: both 07_end_users.sql
and 13_dds_variant.sql had SET DEFINE ON without SET VERIFY OFF,
which causes sqlplus to echo the substituted DDL (including the
IDENTIFIED BY <password> clause) on the `new 1:` line. Added
SET VERIFY OFF.

E2E re-verified on ADB 23.26.2.2.0: matrix identical to manual run
(MY=17 / PG=12 / BOTH=12+17 / NONE=ORA-00942 on both), no password
in logs, dds-teardown leaves no residue.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
devmrko
2026-05-26 15:53:15 +09:00
parent b7ee325b67
commit 1f4a9c7e64
6 changed files with 191 additions and 8 deletions

View File

@@ -10,6 +10,7 @@
SET ECHO OFF
SET FEEDBACK ON
SET DEFINE ON
SET VERIFY OFF -- 비번이 'new 1:' 라인으로 echo 되는 것을 막음 (leak 방지)
PROMPT === Creating end-user accounts ===
-- Passwords come from .env (DEFINE) so they aren't hardcoded in source.

View File

@@ -51,6 +51,7 @@
SET ECHO OFF
SET FEEDBACK ON
SET DEFINE ON
SET VERIFY OFF -- 비번이 'new 1:' 라인으로 echo 되는 것을 막음 (leak 방지)
PROMPT === 0. Creating DDS-only views (no VPD policy attached) ===
-- Functionally identical to v_customers_pg / v_customers_my but

View File

@@ -0,0 +1,61 @@
-- ============================================================
-- 14_tests_dds_user.sql
-- Shared verification script for all 4 DDS end-users.
-- Run as ddsuser_my / ddsuser_pg / ddsuser_both / ddsuser_none.
--
-- Expected matrix (DDS):
-- user v_dds_customers_pg v_dds_customers_my
-- --------------- --------------------- ---------------------
-- ddsuser_my ORA-00942 (hidden) 17 rows
-- ddsuser_pg 12 rows ORA-00942 (hidden)
-- ddsuser_both 12 rows 17 rows
-- ddsuser_none ORA-00942 (hidden) ORA-00942 (hidden)
--
-- Note: where VPD silently returns "0 rows", DDS returns
-- `ORA-00942 (table or view does not exist)` — the object is
-- not visible at all. `WHENEVER SQLERROR CONTINUE` is set so the
-- script keeps going past expected ORA-00942 errors.
-- ============================================================
WHENEVER SQLERROR CONTINUE
SET FEEDBACK ON
SET LINESIZE 200
SET PAGESIZE 100
PROMPT
PROMPT === Who am I? (DDS end-user context) ===
SELECT ORA_END_USER_CONTEXT.username AS end_user_name FROM dual;
PROMPT
PROMPT === Row counts on the DDS-only views ===
PROMPT (ORA-00942 here means "no DATA GRANT" that's the expected deny path)
SELECT 'v_dds_customers_pg' AS view_name, COUNT(*) AS rows_visible
FROM admin.v_dds_customers_pg;
SELECT 'v_dds_customers_my' AS view_name, COUNT(*) AS rows_visible
FROM admin.v_dds_customers_my;
PROMPT
PROMPT === Sample (first 3 rows from each view, if visible) ===
COLUMN customer_id FORMAT 9999
COLUMN full_name FORMAT A20
COLUMN email FORMAT A30
COLUMN region FORMAT A8
SELECT customer_id, full_name, email, region
FROM admin.v_dds_customers_pg
ORDER BY customer_id FETCH FIRST 3 ROWS ONLY;
SELECT customer_id, full_name, email, region
FROM admin.v_dds_customers_my
ORDER BY customer_id FETCH FIRST 3 ROWS ONLY;
PROMPT
PROMPT === Bypass attempts ===
PROMPT -- 1) underlying VPD view (no GRANT given) -> must fail
SELECT COUNT(*) FROM admin.v_customers_pg;
SELECT COUNT(*) FROM admin.v_customers_my;
PROMPT -- 2) raw remote table via DB Link (no GRANT on the link) -> must fail
SELECT COUNT(*) FROM "public"."customers"@RDS_POSTGRES_LINK;
SELECT COUNT(*) FROM "ecommerce_poc"."customers"@RDS_LINK;
PROMPT -- 3) permission / app_user tables (VPD plane) -> must fail
SELECT COUNT(*) FROM admin.permission;
SELECT COUNT(*) FROM admin.app_user;
EXIT;

View File

@@ -0,0 +1,53 @@
-- ============================================================
-- 15_dds_cleanup.sql
-- Idempotent teardown for the DDS variant (13_dds_variant.sql).
-- Safe to run before 13_dds_variant.sql to wipe partial state.
-- Errors are ignored (objects may not exist on first run).
-- Run as ADMIN.
-- ============================================================
WHENEVER SQLERROR CONTINUE NONE;
SET ECHO OFF
SET FEEDBACK OFF
PROMPT === Dropping DDS data grants (if present) ===
BEGIN EXECUTE IMMEDIATE 'DROP DATA GRANT admin.dds_my_only_grant_mysql'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
BEGIN EXECUTE IMMEDIATE 'DROP DATA GRANT admin.dds_pg_only_grant_pg'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
BEGIN EXECUTE IMMEDIATE 'DROP DATA GRANT admin.dds_both_grant_pg'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
BEGIN EXECUTE IMMEDIATE 'DROP DATA GRANT admin.dds_both_grant_mysql'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
PROMPT === Dropping DDS end users (no CASCADE END USERs own no objects) ===
BEGIN EXECUTE IMMEDIATE 'DROP END USER "ddsuser_my"'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
BEGIN EXECUTE IMMEDIATE 'DROP END USER "ddsuser_pg"'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
BEGIN EXECUTE IMMEDIATE 'DROP END USER "ddsuser_both"'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
BEGIN EXECUTE IMMEDIATE 'DROP END USER "ddsuser_none"'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
PROMPT === Dropping DDS data roles (if present) ===
BEGIN EXECUTE IMMEDIATE 'DROP DATA ROLE my_only_role'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
BEGIN EXECUTE IMMEDIATE 'DROP DATA ROLE pg_only_role'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
BEGIN EXECUTE IMMEDIATE 'DROP DATA ROLE both_sources_role'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
BEGIN EXECUTE IMMEDIATE 'DROP DATA ROLE connect_only_role'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
PROMPT === Dropping DDS regular role (session-carrier) ===
BEGIN EXECUTE IMMEDIATE 'DROP ROLE dds_db_role'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
PROMPT === Dropping DDS-only views ===
BEGIN EXECUTE IMMEDIATE 'DROP VIEW v_dds_customers_pg'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
BEGIN EXECUTE IMMEDIATE 'DROP VIEW v_dds_customers_my'; EXCEPTION WHEN OTHERS THEN NULL; END;
/
PROMPT === DDS cleanup complete ===
EXIT;