Implementing ISO/IEC 42001 Annex A.4 and A.7: tool sandboxing, data quality, and egress redaction
- Published
- Data
- 53 MCP tools, 14 RAG connectors, 4.31M indexed chunks, 37 monitored data pipelines, 323 replayed tool outputs
- Figures
- 4
The claim
When an organization connects an AI agent to internal databases and logs, governance moves from policy documents to infrastructure boundaries. In ISO/IEC 42001 (2023), two Annex A domains govern this layer. Annex A.4 (5 controls, A.4.2 to A.4.6) covers tooling and compute resources. Annex A.7 (5 controls, A.7.2 to A.7.6), paired with ISO/IEC 5259-1 (2024) to 5259-5 (2025) and EU AI Act Articles 10 and 15, covers data provenance, quality, and preparation.
Standard software guards often fail silently on agent workloads. If a SQL tool checks queries with regular expressions, a prompt injection or model error can still read local files or environment secrets. If a retrieval pipeline only checks job exit codes, an unrecorded model change can leave 99.12% of vectors (2,128,771 of 2,147,651) in the wrong space. Naive word-boundary regexes miss 81.8% of compound secret keys (27 of 33). Conversely, unfiltered cloud data-loss detectors falsely mask 100.0% of technical identifiers (18 of 18).
This report is Part 2 of our ISO/IEC 42001 (2023) series (Part 1). It details three production controls across 53 agent tools, 14 retrieval connectors (4.31M chunks), and 4 output paths.
First, we parse every agent query through the database engine's own C++ syntax tree (3 sandbox layers) and enforce 7 read-only confinement layers in PostgreSQL. Second, we apply ISO/IEC 5259-1 (2024) data quality rules across 37 monitored pipelines, combining quality floors (0.4), vector count checks, and BLAKE3 hashes. Third, we pair 33 redaction rules with a 32-character context filter and a 29.1 ms local firewall, reaching 99.4% recall across 323 tool outputs.
Four technical controls to deploy first
- Validate agent SQL with the database engine's own parser, then lock the configuration. Regular expressions on
read_parquet(...)do not blockread_csv,read_text, orgetenv. Parse every query into a serialized abstract syntax tree (AST), allow only approved table functions and directory prefixes (3prefixes), and runSET lock_configuration = true. - Stamp every vector row with its embedding model, version, and
BLAKE3content hash. A green cron job does not prove retrieval integrity (Breck et al., 2019). Recording model provenance and checking batch vector counts on every write prevents silent vector-space drift across millions of rows. - Remove leading word-boundary anchors (
\b) from secret-key patterns. Standard\b(password|secret)\bpatterns miss 27 of 33 (81.8%) camelCase and prefixed keys in logs and command lines. Match secret suffixes without a leading word boundary while exempting file-pointer keys. - Filter cloud DLP detections against a 32-character technical context window. Default cloud detectors mistake software versions for IP addresses and transaction IDs or backup timestamps for phone numbers (
18of18masked). Reclassify matches adjacent to technical keywords (uuid,rpc,txn, or path slashes) so domain identifiers survive intact (0of18lost).
Annex A.4.4 and A.4.5: sandboxing agent SQL tools and archive extraction
Annex A.4.4 of ISO/IEC 42001 (2023) and EU AI Act Article 15(5) require resilience against prompt injection and confidentiality attacks. An agent's analytical tools must therefore enforce least privilege at the parser and kernel boundaries.
1. The 3-layer C++ AST SQL sandbox
When an AI agent queries a columnar lakehouse (such as DuckDB over Parquet), teams often validate paths inside read_parquet(...) with a regular expression. Auditing that pattern showed that an adversarial or hallucinated query can bypass regex checks by calling other table functions (read_csv('/etc/passwd'), read_text(...), glob('*'), or duckdb_secrets()).
To close that class of escapes in any SQL-backed agent tool, we implemented a 3-layer defense-in-depth sandbox.
-- Layer 3 engine lockdown executed before any agent SQL statement:
SET allowed_directories = ['/cases', '/var/data/diagnostic-artifacts', '/mnt/diagnostic-artifacts'];
SET enable_external_access = false;
SET autoinstall_known_extensions = false;
SET autoload_known_extensions = false;
SET lock_configuration = true;
- Layer 1 (Lexical pre-screen). The validator rejects queries with more than 1 semicolon, strips string literals and comments, and requires a
SELECT,WITH, orDESCRIBEprefix. It blocks 15 DDL, DML, and administrative tokens (attach,call,copy,create,delete,detach,drop,insert,install,load,merge,pragma,truncate,update,vacuum). - Layer 2 (Authoritative C++ syntax-tree walk). The validator parses the SQL using the database engine's own C++ parser via
duckdb.extract_statements()andSELECT json_serialize_sql(?). Walking the serialized JSON AST verifies five rules. First, it requires exactly 1SELECTstatement. Second, every table source must be a declared CTE or 1 of 2 allowed Parquet functions (read_parquetorparquet_scan). Third, every file argument must be a constant string literal under 1 of 3 approved directory prefixes. Fourth, 0..path-traversal segments are permitted. Fifth, 3 secret-inspection functions (getenv,current_setting,which_secret) and all other table functions are rejected. - Layer 3 (Engine configuration lock and resource caps). Before executing the verified query, the session runs the 5 lockdown statements above. Once
SET lock_configuration = trueruns, no subsequent SQL expression can enable external access or load extensions. Each query is capped at 2,000 rows, 2 MiB of output text, and 5,000 Parquet files per glob. Runtime is capped at 270 to 300 seconds, with 512 MB across 2 threads on in-process workers.

2. Seven-layer PostgreSQL confinement, tool allowlists, and archive guards
We paired the columnar AST sandbox with three additional Annex A.4.4 and A.4.5 resource controls:
- Seven-layer relational database confinement. Agent queries to PostgreSQL connect through a dedicated read-only role with
default_transaction_read_only = on. Every call runs inside an explicit read-only transaction and blocks access to raw embedding tables. It also enforces a 30-token blocklist (pg_sleep*,set_config,pg_read_file,dblink*) and caps output at 500 rows and 30 to 60 seconds. - Per-agent tool allowlists and call budgets. Rather than exposing all 53 MCP tools to every background workflow, our intake agent is restricted to an explicit 20-tool allowlist. Total tool calls are capped at 10 per run (
3search calls maximum), identical(tool, args)calls are deduplicated, and remote operations require human approval. - Archive-bomb and sensitive-file blocking. When unpacking diagnostic archives on ephemeral batch VMs (
64to2,000GB dynamic disk), our extraction guard enforces 6 limits. It caps archives at2,000,000files,16GiB per file, and64GiB total (or70%of dynamic disk). It also enforces a7,200to21,600second timeout, rejects all symbolic or hard links (0allowed), and verifies target-directory containment. At ingestion, the pipeline also blocks 4 classes of sensitive artifacts:.sqldatabase dumps,.hprofheap dumps,.pemprivate keys, and.envfiles.
Annex A.7 and ISO/IEC 5259: preventing silent data corruption in RAG pipelines
ISO/IEC 42001 (2023) Annex A.7 (A.7.2 to A.7.6) requires documented controls for data acquisition, quality, provenance, and preparation. Normative note Clause 3.25 points directly to the ISO/IEC 5259 series (ISO/IEC 5259-1 (2024) overview, 5259-2 (2024) quality measures, 5259-3 (2024) management requirements, 5259-4 (2024) process framework, and 5259-5 (2025) governance).
In production retrieval systems, standard job monitoring misses four classes of silent data corruption (Breck et al., 2019; Sculley et al., 2015).
| silent data corruption mode | what standard job monitoring showed | what data-quality & provenance auditing found | ISO/IEC 5259 & Annex A.7 control implemented |
|---|---|---|---|
Vector-space drift (A.7.5) |
Ingestion jobs exited 0 with HTTP 200 | An unrecorded model transition left 2,128,771 of 2,147,651 stored vectors (99.12%) in the prior embedding space |
Every vector row stamps embedding_model, embedding_version (2), BLAKE3 content hash, and ingestion timestamp |
Upstream schema-field drift (A.7.3 / A.7.4) |
Issue-tracker sync logged 1,023 consecutive green runs |
Reading a standard field instead of a custom field overwrote the component column with NULL on 1,546 escalations (25 of 25 recent cases) |
Restored custom-field binding and added non-null column completeness checks alongside 37 SQL freshness contracts |
Batch cardinality truncation (A.7.4 / A.7.6) |
HTTP 200 + zip() committed without error (report 05) |
Bare-string multimodal calls returned 1 vector per batch, dropping 85.1% to 96.9% of chunks |
Boundary assertion raises an exception whenever len(vectors) != len(texts) |
Feedback-loop self-citation (A.7.2 / EU AI Act Art. 15(4)) |
Knowledge-base sync indexed all markdown files | Later agent runs retrieved and cited prior AI-generated notes as ground truth | Ingestion connector excludes AI-generated notes directories from indexing |

We operationalized ISO/IEC 5259-2 (2024) and 5259-4 (2024) across our 14 retrieval connectors (4.31M chunks). Prose chunks split at 1,000 characters (200 overlap) and code chunks split at 2,043 characters (100 overlap). Four deterministic gates govern ingestion.
- Pre-embedding chunk quality filter (
floor = 0.4). Every candidate chunk starts at a score of 1.0. The scorer multiplies by 0.6 for under 15 words and 0.3 for under 7 words. It multiplies by 0.5 for a unique-word ratio below 0.25 and 0.4 for link density above 0.6. It subtracts 0.2 for each of 6 boilerplate patterns. Any chunk scoring below 0.4 is dropped before embedding. - Boundary cardinality check and circuit breaker. Every embedding call asserts
len(vectors) == len(texts)before writing to the store. A circuit breaker aborts a sync after 3 consecutive failed documents. It retries429or503API errors up to 5 times (10to120s backoff). - Cryptographic change detection (
BLAKE3andSHA-256). Every text chunk stores aBLAKE3content hash. Every diagnostic archive stores itsSHA-256digest under a composite unique key, deduplicating fleet views from10,700to10,213rows. In customer disclosures, we distinguish incremental change detection (BLAKE3) from immutable storage locking. - 37-source SQL freshness contracts. A PostgreSQL contract table monitors
age / cadence, late thresholds, and stale thresholds across all 37 pipelines. Those 37 pipelines cover 10 relational, 14 RAG, 7 archive, and 6 trace feeds. Quiet event streams (such as incident paging) are evaluated by sync-job heartbeat rather than last-event age.
Annex A.7.6: two-layer egress redaction and the 29.1 ms monotonic firewall
Operational tickets and logs routinely contain database passwords, cloud API keys, and personal data. Under Annex A.7.6 (Data preparation), production enforces our 33-rule redactor plus regional Cloud DLP (technical_context keep-filter) across all 4 output paths. Deployment-workflow and configuration-code pull requests pin that Cloud Run enforcement across services.
1. Fixing the \b compound-secret leak and Cloud DLP identifier masking
Testing our redaction pipeline against production telemetry uncovered two opposite failure modes that affect any technical enterprise.
- Why word-boundary
\bregexes missed81.8%of compound secrets (27of33). Standard 8-pattern secret redactors use\b(password|passwd|secret|token|...)\b. Because underscores and ASCII letters are word characters in regexes, a leading\bfails on prefixed or camelCase keys (ldapbindpasswd=,db_password=,clientSecret:). An offline regex-design scan of historical process-table snapshots (pidstat) uncovered113,000command-linepassword=orsecret=arguments and14,911Java-pw VALUEflags. That scan motivated our 33-rule prefix-agnostic redactor (14key rules and19value rules) before indexing or egress. We removed the inner\bboundary, added CLI flag parsers (--password,-pw,mysql -pVAL,cqlsh -p VALwhile preservingpsql -p <port>), and exempted file-pointer keys (vaultPasswordFile=/etc/x). Catch rate on the 33 compound secret patterns rose from 18.2% (6of33) to 100.0% (33of33). - Why unfiltered Cloud DLP masked
100.0%of technical identifiers (18of18). When we enabled regional Google Cloud DLP, its defaultPHONE_NUMBER,IP_ADDRESS, andENCRYPTION_KEYdetectors falsely masked 18 of 18 case-critical technical identifiers on a real incident. It turned release strings (v2.20.1) into[IP_ADDRESS], backup filenames (backup_26-10-11-07-00.tgz) and RPC call IDs into[PHONE_NUMBER], and replication stream IDs into[ENCRYPTION_KEY]. Under its 100-RPM quota, Cloud DLP also stalled for 57 to 59 seconds on 10.3% of calls.
We solved the identifier-masking defect by adding a deterministic 32-character technical_context keep-filter. Any PHONE_NUMBER, DATE_OF_BIRTH, CREDIT_CARD_NUMBER, or ENCRYPTION_KEY match containing a path slash or UUID is preserved as a technical identifier. Matches within 32 characters of a technical keyword (id, uuid, pid, tid, request, rpc, txn, op_id, term, tablet, peer, ht, priority) are preserved too. Across all 18 technical identifiers, false masking dropped from 100.0% (18 of 18) to 0.0% (0 of 18 lost).

2. Evaluating the two-stage monotonic privacy firewall at 29.1 ms
To eliminate the 10.3% Cloud DLP quota stalls, we evaluated a two-stage firewall. It pairs our 33 deterministic rules (Layer 3a) with a local 8-bit 4B model (Layer 3b). That local model runs at 29.1 ms median GPU latency (report 07). Both stages combine under a monotonic logical OR rule (Layer 3a OR Layer 3b).
Because the union is monotonic, the neural stage (Layer 3b) can add redactions on contextual secrets. It can never unmask a token matched by the 33 deterministic rules (Layer 3a). On our 77-item torture benchmark (49 secrets and PII, 28 technical IDs), the firewall caught 100.0% of secrets (49 of 49). It produced 0.0% false positives on the 18 database IDs. Evaluated across 323 replayed MCP tool outputs, the monotonic union achieved 99.4% recall at a 0.42% false-positive rate (F1 = 0.995) with 0.0% quota stalls.

Verification gates and methodology checks
- C++ AST SQL bypass suite. Tested multi-statement injections, CTE shadowing,
read_csv,read_text,glob,duckdb_secrets,getenv,..traversal, and runtimeSEToverrides against the 3-layer validator (100%blocked). - Vector-space provenance audit (
2,147,651vectors). Audited the vector store by model stamp, confirming that stampingembedding_modelandembedding_version(2) plus strict batch-count checks prevents mixed-space indexing and silent truncation. - Technical identifier preservation check (
18technical IDs,323tool outputs). Verified that the 32-charactertechnical_contextkeep-filter preserves18of18software versions, UUIDs, backup filenames, and RPC IDs while keeping secret recall at100.0%. - Quantization floor on the local privacy gatekeeper. In report 07, 4-bit quantization flipped 5.4% to 8.7% of borderline safety decisions. Our local
29.1ms firewall therefore enforces an 8-bit (Q8_0) minimum floor.
Tooling and data antipatterns rejected by our gates
- Regex-only SQL path validation on
read_parquet(...). Replaced by the 3-layer C++ AST walk andSET lock_configuration = true. - Judging data quality by cron job exit codes (
0errors). Replaced byISO/IEC 5259-1(2024) provenance stamps (BLAKE3+embedding_model), non-null column checks, and 37 SQL freshness contracts. - Word-boundary
\bsecret-key regexes (81.8%miss rate on compound keys). Replaced by 33 prefix-agnostic key and value rules and CLI flag parsers. - Unfiltered Cloud DLP on technical telemetry (
100.0%false masking on18technical IDs and10.3%quota stalls). Fixed via the 32-charactertechnical_contextkeep-filter and the29.1ms monotonic OR firewall. - Allowing RAG connectors to index prior AI-generated case notes. Blocked by excluding AI-generated note directories from ingestion.
Pressure tests
Every finding was checked against the six standing fronts in Methods.
| front | what was tested | result |
|---|---|---|
| Specification | Regex SQL filtering vs native C++ AST serialization (json_serialize_sql) + lock_configuration |
Regex missed read_csv, glob, and duckdb_secrets; 3-layer AST + engine lock blocked 100% of non-Parquet sources |
| Aggregation | Pipeline-level "green heartbeat" (1,023 sync runs) vs column-level completeness |
Heartbeat masked 1,546 nulled escalation rows; column-level check restored 100% component population |
| Adjustment coarseness | Unfiltered Cloud DLP vs 32-character technical_context hotword keep-filter |
Unfiltered DLP masked 18 of 18 technical IDs; 32-char keep-filter reduced false masking to 0 of 18 (0.0%) |
| Prior work | ML data validation (Breck et al., 2019), CACE entanglement (Sculley et al., 2015), and ISO/IEC 5259-1 (2024) |
Extended to vector-space model stamping (2.13M vectors), batch cardinality assertions, and monotonic DLP firewalls |
| Baseline sufficiency | Deterministic 33-rule redactor alone (Layer 3a) vs monotonic union (n=323 outputs) |
Monotonic union guaranteed recall >= Layer 3a, reaching 99.4% recall and 0.42% FPR (F1 = 0.995) at 29.1 ms |
| Cross-sectional units | Quiet event streams vs high-frequency ticket streams across 37 SQL freshness contracts |
Separated sync-pipeline heartbeat age from last-event age so quiet alert channels do not trigger false staleness |
What this does not show
First, egress redaction at the tool layer (4 of 4 output paths) protects model and user outputs. Upstream storage defense-in-depth combines pre-embedding sanitization on high-sensitivity connectors with strict database role confinement (default_transaction_read_only = on and blocking direct SQL access to raw vector tables). Expanding pre-embedding sanitization across all 14 connectors is tracked on our governance roadmap.
Second, our heuristic chunk quality filter (floor = 0.4) scores prose and code chunks prior to vectorization. Multimodal attachments (screenshots and PDFs passed to multimodal models) bypass text chunking and are governed under a separate roadmap action.
Reproduce
All aggregate metrics in this report and its four figures are published in numbers.json, with SHA-256 file digests recorded in MANIFEST.md.
References
- Breck, E., Polyzotis, N., Roy, S., Whang, S., & Zinkevich, M. (2019). Data Validation for Machine Learning. Proceedings of MLSys 2019.
- Brundage, M., Avin, S., Wang, J., Belfield, H., Krueger, G., Hadfield, G., et al. (2020). Toward Trustworthy AI Development: Mechanisms for Supporting Verifiable Claims. arXiv preprint. arXiv.
- CEN/CENELEC. (2026).
EN 18286(prEN 18286): Artificial Intelligence - Quality Management System for EU AI Act Regulatory Purposes. European Committee for Standardization. - European Parliament & Council of the European Union. (2024). Regulation (EU)
2024/1689Laying Down Harmonised Rules on Artificial Intelligence (Artificial Intelligence Act). Official Journal of the European Union. - ISO/IEC. (2023).
ISO/IEC 42001(2023): Information Technology - Artificial Intelligence - Management System. International Organization for Standardization. - ISO/IEC. (2024).
ISO/IEC 5259-1(2024) to5259-5(2025): Artificial Intelligence - Data Quality for Analytics and Machine Learning (ML), Parts 1 to 5. International Organization for Standardization. - Mökander, J., Schuett, J., Kirk, H. R., & Floridi, L. (2023). Auditing Large Language Models: A Three-Layered Approach. AI and Ethics. DOI.
- Sculley, D., Holt, G., Golovin, D., Davydov, E., Phillips, T., Ebner, D., et al. (2015). Hidden Technical Debt in Machine Learning Systems. Proceedings of NeurIPS 2015.