Skip to content

SQL injection via unescaped metadata/filter value in the collection-database layer (cross-document disclosure) #1304

Description

@geo-chen

Summary

llmware's SQLite and PostgreSQL collection-database backends build WHERE clauses by string-interpolating filter and lookup values directly into SQL, with no parameterization or escaping. A filter value containing SQL metacharacters injects into the query, neutralizing the intended filter and returning rows the caller was scoped out of (cross-document / cross-collection disclosure), and on PostgreSQL enabling full boolean/UNION-based injection. Confirmed against the real SQLiteRetrieval.filter_by_key_dict: a benign filter returned one row, while an injected value returned a different, secret document.

Details

In llmware/resources.py, the SQLite retrieval methods concatenate the value into the SQL string:

# SQLiteRetrieval.filter_by_key_dict (~line 2339)
conditions_clause += f" {key} = '{value}' AND "
# SQLiteRetrieval.text_search_with_key_value_dict_filter (~line 2280)
... f" AND {key} = '{value}'"

The PostgreSQL backend has the identical pattern (resources.py ~lines 1200 and 1239), with no FTS5 MATCH restriction, so tautology and UNION payloads are unrestricted there. The filter validator (llmware/retrieval.py ~lines 549 to 553) checks only the key against an allowed-keys list; the value is never sanitized. The table/library name is guarded by safe_name, but values are not.

These value sinks are reached from the public API: Library.block_lookup(block_id, doc_id) -> filter_by_key_dict (library.py ~line 1265), and Query.text_query_with_custom_filter / text_query_by_author_or_speaker -> text_search_with_key_value_dict_filter (retrieval.py ~lines 532 and 540). In a RAG or multi-tenant deployment these values derive from end-user query input or from document/metadata identifiers.

Happy to share a poc.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions