Skip to content

Escape the values in the duckdb secret statements - #1446

Merged
andredlng merged 1 commit into
mainfrom
fix-duckdb-secret-quoting
Jul 28, 2026
Merged

Escape the values in the duckdb secret statements#1446
andredlng merged 1 commit into
mainfrom
fix-duckdb-secret-quoting

Conversation

@andredlng

Copy link
Copy Markdown
Collaborator

Summary

The S3, GCS and Azure credentials are handed to duckdb with CREATE SECRET, built by string interpolation. Two of the interpolated values come from the contract rather than the environment:

  • server.endpointUrl — used for MinIO and other S3-compatible stores
  • the storage account derived from server.location for Azure

A data contract can be a URL someone else published, and testing one is a documented workflow — the quickstart does exactly that.

The schema check does not stop it

endpointUrl is validated as format: uri, which rejects an obvious payload containing a space. But a single quote is a legal sub-delimiter in RFC 3986, so a valid URI can carry one. With this contract:

servers:
  - server: production
    type: s3
    location: s3://bucket/orders/*.csv
    format: csv
    endpointUrl: "http://a',SCOPE,'b"

datacontract test reported:

Data Contract Tests: Parser Error: syntax error at or near ","

The quote ended the string literal and duckdb parsed the remainder as SQL. I stopped at establishing that — no exploit chain was built — but duckdb can read and write local files and install extensions, so the ceiling is high for something reachable by testing a third-party contract.

After the fix the same contract yields:

IO Error: URL using bad/illegal format or missing URL error for HTTP GET to
'http://a',SCOPE,'b/bucket/?encoding-type=url&list-type=2&prefix=orders%2F'

— the payload is now a literal endpoint string, rejected by the HTTP layer, with the SQL intact.

The fix

One helper escaping single quotes, applied to all 26 interpolations across the three setup_*_connection functions — credentials, region, endpoint, URL style, tenant and client ids, and the derived storage account. Credentials are escaped too: an access key containing a quote would previously have broken the statement.

Testing

Two new tests: a quote in endpointUrl stays inside the literal, and a quote in a credential is escaped. The existing test that a custom endpoint still uses path-style addressing continues to pass, so the MinIO feature is unaffected.

The MinIO-backed suites could not run locally (no Docker daemon at the time); CI covers them.

The credentials for S3, GCS and Azure are stored with CREATE SECRET, built by
interpolation. Two of the values come from the contract rather than the
environment: endpointUrl, and the storage account derived from location. A
contract can be a URL someone else published, and testing one is a documented
workflow.

A single quote is a legal URI sub-delimiter, so the schema's format: uri check
passes it through. It then ended the string literal and duckdb parsed the rest
as SQL, reported as a parser error at the injected text.

Every interpolated value is escaped now.
@andredlng andredlng self-assigned this Jul 28, 2026
@github-actions

Copy link
Copy Markdown

📖 Docs preview

Preview site: https://brave-water-0cee36e03-1446.westeurope.7.azurestaticapps.net

Changed pages (most-changed first):

@andredlng
andredlng merged commit f175df2 into main Jul 28, 2026
16 checks passed
@andredlng
andredlng deleted the fix-duckdb-secret-quoting branch July 28, 2026 14:34
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.

1 participant