Skip to content

SQLiteSession serves edited, replayed and re-owned rows as genuine history; get_items() never signals a changed store #5176

Description

@tech4biz-yasha

Measured on openai-agents 0.20.0, macOS arm64, Python 3.12, with the open MIT suite agmi (https://github.com/tech4biz-yasha/agmi, row openai-agents-sqlite-session).

Method: seed a session through SQLiteSession.add_items(), apply one edit directly to the SQLite file with no keys, open a fresh SQLiteSession on the same file, read through get_items(), and record what the SDK does.

Result: all eight storage-level edits are accepted, meaning get_items() returns without raising and the edited history is fed to the model on the next run.

T1 text changed inside message_data
T2 newest rows deleted, so the agent resumes from an older state
T3 one row removed from the middle
T4 two rows swapped
T5 a new row inserted by the attacker
T6 another session's row copied over this session's row, session_id kept (user B's item served as user A's)
T7 an older row of the same session copied over the newest
T8 the row's created_at rewritten, text untouched. Separately, rewriting a row's session_id moves that item into another user's history, also with no error.

One detail worth noting: a row whose JSON no longer decodes is skipped silently in get_items(), so even a corrupting edit does not surface.

Reproduction script and pinned tests: https://github.com/tech4biz-yasha/agmi/blob/main/tests/test_openai_agents_session.py
Explainer per edit: https://agentmemoryintegrity.org/edits.html

I realise the store is a local file and the threat model may be "trust the filesystem". Two things would still help users who put this behind a shared volume or a backup: a note in the SQLiteSession docs saying the store carries no integrity protection, and optionally a per-row MAC over session_id + position + message_data with a keyed verify on read. The reference at-rest store in agmi (agmi/adapters/reference_atrest.py) shows the minimum that rejects all eight. Happy to open a PR for either, and I will re-measure and update the public row when anything changes.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions