CVE-2026-55098Medium· 6.5▾ SunlitArc: DuckDB I/O aliases bypass database RBAC after CVE-2026-47735
▾ Sunlit zone — Low / medium · no exploitation signal
impact 35.8 · likelihood 0.1 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Exploit-prediction probability, daily snapshots since Nov 4.
Disclosure to exploitation, from the record and what we observed since indexing it.
Disclosed via OSV
0.3%
The fix for CVE-2026-47735 (released 2026.06.1) is incomplete. It added a DuckDB sandbox (SET GLOBAL allowed_directories=[<storage root>]; SET GLOBAL enable_external_access=false) and an Arc SQL-layer denylist of the literal functions read_parquet( and arc_partition_agg(. The sandbox correctly blocks reads outside the storage root (OS files, http:// SSRF), but it allowlists the entire multi-tenant storage root, and the SQL denylist still misses the rest of the DuckDB I/O family the original advisory enumerated. As a result, any authenticated token — including one with permissions: [] — can read or enumerate every other database's data within the deployment, defeating RBAC.
Verified at HEAD c08b892 (v26.06.1-36, includes 26.06.2). Confirmed still live; no later commit blocks these functions.
internal/database/sandbox.go:111 allowlists cfg.LocalStorageRoot (default storage.local_path=./data/arc). Every database lives under it — internal/storage/util.go:33 GetStoragePath => <base>/<database>/<measurement>/**/*.parquet. Any in-root path is sandbox-permitted regardless of which database owns it.checkQueryPermissions (internal/api/query.go:1181) extracts table refs via extractTableReferences, which skips any name followed by ( as a function (internal/api/query.go:1116); with zero refs it returns allow (:1213-1216). So parquet_scan(...), glob(...), parquet_metadata(...), read_blob(...), read_csv_auto(...) are never authorization-checked.userSQLReadParquetPattern (internal/api/query.go:2087) is \bread_parquet\s*\(. DuckDB's documented alias parquet_scan( is not matched, and read_csv_auto/read_json/read_text/read_blob/glob/parquet_metadata/parquet_schema are not blocked anywhere. The fix author documented this very threat at internal/api/query.go:2061-2064 (“a user scoped to db1.m1 could read /data/db2/secrets/**/*.parquet”) but blocked only the literal read_parquet(.POST /api/v1/query
Authorization: Bearer <token>
Content-Type: application/json
{"sql": "SELECT * FROM parquet_scan('./data/arc/<victim-db>/<measurement>/**/*.parquet')"}
ValidateSQLRequest passes (parquet_scan( is not read_parquet(, not a dangerous keyword, single statement).checkQueryPermissions extracts 0 table refs (it is a function call) and allows.convertSQLToStoragePaths finds no read_parquet literal and no bare FROM measurement to rewrite, so the query passes through unchanged.parquet_scan('<root>/<victim-db>/...'); the path is inside allowed_directories, so the other database's rows are returned.glob('./data/arc/**/*') enumerates every database/measurement/file; parquet_metadata(...)/parquet_schema(...) leak column names and per-column min/max value statistics; read_blob(...) returns raw file bytes. All operate within the allowlisted root.
Reproduces Arc's exact sandbox config (allowed_directories=[<root>], enable_external_access=false) on DuckDB 1.5.x (Arc pins duckdb-go-bindings 1.5.1) with a victim parquet written under the root:
[BLOCKED ] read_blob OUTSIDE root -> Permission Error: Cannot access file ...
[EXECUTES] parquet_scan victim (read_parquet ALIAS) -> [('tenant-VICTIM-row', 42)]
[EXECUTES] glob enumerate every db under root -> [('.../data/arc/victim-db/secrets/f.parquet',)]
[EXECUTES] parquet_metadata victim -> [('leaked','tenant-VICTIM-row','tenant-VICTIM-row'), ('v','42','42')]
The sandbox blocks escape (as designed) but permits every in-root cross-database read.
Cross-database / cross-tenant confidential read by any authenticated principal, including a least-privileged (permissions: []) token, on RBAC-enabled deployments. This is the same RBAC-bypass impact the original advisory described for read_parquet, still reachable via the alias and the unblocked I/O family. (Arc's SQLite token DB ./data/arc.db is a sibling of ./data/arc/ and is not in the allowlist, so it is not readable this way — the exposure is the time-series data of every database, plus its schema and value-range statistics.)
Per-request scope the sandbox to the requesting token's authorized database path(s): allowed_directories = ['<base>/<authorized-db>/'], so the file layer cannot cross the RBAC boundary regardless of which function is used. Alternatively, replace the function-name denylist with a strict allowlist of permitted table functions (alias-resolved, reject all others). A denylist of literal spellings is structurally fragile — this is the second alias/family miss after the original audit.
github.com/basekick-labs/arc < 0.0.0-20260610131716-5dc8f53b70a3Upgrade to a patched release:
github.com/basekick-labs/arc 0.0.0-20260610131716-5dc8f53b70a3Connected by shared product, vendor, weakness, or advisory.
GHSA-m3qr-fvp4-78xjLowArc has unauthenticated application log disclosure via `/api/v1/logs`
CVE-2026-47735HighArc is an open, SQL-native time-series database for telemetry
CVE-2026-55678MediumArc is an open, SQL-native time-series database for telemetry
CVE-2022-0577Medium· 6.5Incorrect Authorization and Exposure of Sensitive Information to an Unauthorized Actor in scrapy
CVE-2025-12149NoneIn Search Guard FLX versions 3.1.2 and earlier, while Document-Level Security (DLS) is correctly enforced elsewhere, when the search is triggered from a Signals watch, the DLS rule is not enforced, allowing access to all documents in the…
CVE-2021-38554Medium· 5.3vault: UI erroneously cached and exposed user-viewed secrets between sessions in a single shared browser (CVE-2021-38554)