Skip to content

fix(sec-core): prevent adapter bytecode drift - #2265

Draft
kongche-jbw wants to merge 1 commit into
alibaba:mainfrom
kongche-jbw:fix/sec-core/adapter-bytecode-drift
Draft

fix(sec-core): prevent adapter bytecode drift#2265
kongche-jbw wants to merge 1 commit into
alibaba:mainfrom
kongche-jbw:fix/sec-core/adapter-bytecode-drift

Conversation

@kongche-jbw

Copy link
Copy Markdown
Collaborator

Why

sec-core Python hooks can create __pycache__/*.pyc inside digest-managed
adapter resource roots. The next anolisa adapter status then reports a
healthy adapter as degraded even though no managed source or manifest changed.

The digest must continue covering every managed byte, so this fixes the writer
and cleans historical runtime caches instead of weakening integrity checks.

What changed

  • Run the Qwen, Codex, Qoder, and cosh Python hooks with -B.
  • Add a bounded cache cleaner for sec-core adapter roots and use it in RPM and
    raw packaging/install flows.
  • Execute strict incoming post_install hooks during raw updates, resolving
    them before host mutation and rolling back on failure.
  • Keep the digest encoding and coverage unchanged while consolidating the
    shared digest implementation.
  • Add regressions for cache safety boundaries, raw upgrade cleanup, early hook
    resolution, strict-hook rollback, and ordinary bundle drift detection.

Related issue

Closes #2252

User / Agent impact

Normal sec-core hook execution no longer changes adapter health. Upgrading a
historical raw or RPM installation removes safe derived bytecode caches from
the six contract-owned adapter roots. Source, manifest, symlink, orphan
bytecode, and foreign payload changes remain detectable.

Risk and compatibility

  • Public CLI, API, configuration, or documented behavior changed
  • Privileged or security-sensitive behavior changed
  • Cross-component contract changed
  • Migration or rollback guidance is needed

Cleanup is restricted to sec-core contract-declared adapter roots and only
removes validated Python cache content. A strict cleanup failure aborts and
rolls back the raw update. Raw pre_install and repair replay semantics are
unchanged; only update post_install is wired.

The bundle digest format and receipt schema are unchanged.

Validation

  • cargo fmt --all -- --check
  • cargo check --workspace --locked
  • cargo test --locked --test update_adapter_actions — 8 passed
  • uv run --project agent-sec-cli pytest -q tests/packaging/test_adapter_bytecode.py
    — 18 passed
  • tests/packaging/test-package-raw.sh
  • bash -n for the cleaner, raw hook, package script, and package test
  • Earlier full anolisa gates passed: clippy, workspace tests, and cargo doc.
  • Earlier sec-core suite result: 3535 passed and 3 pre-existing
    time.tzset failures on the local CPython build.
  • rpmspec and rpmbuild are unavailable locally, so RPM macro/parser
    validation remains outstanding.

Documentation and rollback

No user documentation changed. Reverting this commit restores the prior hook
and raw update behavior; the digest and receipt formats need no rollback.

Draft blockers before Ready

  • Bump sec-core from the already-published 0.9.0 in every version-bearing
    file so raw clients can select an actual update and run the cleanup hook.
  • Set min_anolisa_version to the released anolisa version that contains this
    raw post_install support. It must be newer than the affected 0.2.17
    release.
  • Publish the coordinated sec-core artifact through index-v2.toml only;
    keep it out of the legacy index so older raw clients cannot select a package
    whose cleanup hook they do not execute.
  • Run RPM parser/build validation in an environment with
    rpmspec/rpmbuild.

Keep this PR in Draft until the anolisa version, sec-core version, and catalog
publication are synchronized.

- Stop Python hooks from mutating digest-managed adapter resources.
- Sweep historical caches during RPM installation and raw updates.
- Keep full bundle hashing with strict raw post-install hooks.

Fixes: 1a769d3 ("feat(sec-core): qwen plugin and observability hooks")
Signed-off-by: kongche-jbw <kongche.jbw@alibaba-inc.com>
@github-actions github-actions Bot added component:anolisa src/anolisa component:sec-core src/agent-sec-core/ labels Aug 6, 2026
@kongche-jbw

Copy link
Copy Markdown
Collaborator Author

Why this fix uses python3 -B

-B tells CPython not to write bytecode cache files when importing source
modules. It sets the equivalent of sys.dont_write_bytecode = True for that
interpreter process, similar to PYTHONDONTWRITEBYTECODE=1.

It does not disable Python bytecode compilation in memory or change normal
Hook execution. It only prevents the automatic on-disk
__pycache__/*.pyc writes. Existing valid bytecode caches may still be read.

The failure chain in #2252 is:

Hook imports Python modules
  -> CPython writes __pycache__/*.pyc in the adapter resource root
  -> the resource tree changes after enable
  -> anolisa recomputes the complete bundle digest
  -> BundleMatch becomes degraded

The adapter resource root is treated as integrity-managed content. The simplest
way to restore that invariant is therefore to stop normal Hook execution from
mutating it:

python3 -B <hook.py>

This approach was chosen because it:

  • fixes the writer at the source instead of teaching status to reinterpret
    filesystem changes;
  • preserves complete digest coverage, so source, manifest, symlink, orphan
    bytecode, and foreign payload changes remain detectable;
  • uses a standard CPython switch with no digest version, receipt schema, ignore
    glob, or migration format;
  • scopes the behavior to the Hook interpreter rather than exporting a process
    environment variable to unrelated commands;
  • has negligible cost for these short-lived Hooks: the measured difference was
    within run-to-run noise compared with interpreter startup and existing Hook
    timeouts.

-B only prevents future cache creation. It cannot remove caches written by
older releases, which is why the fix also includes the bounded install/update
cleaner. The two parts have separate responsibilities:

  1. python3 -B prevents recurrence.
  2. The strict RPM/raw post-install sweep removes historical derived caches.

For raw upgrades, anolisa must execute the incoming contract's strict
post_install hook; otherwise an existing 0.9.0 installation never receives
the historical cleanup.

We deliberately did not ignore *.pyc in digest_tree. A path or sibling
.py file is not sufficient proof that a bytecode file is benign, and a
blanket exclusion would create a permanent integrity blind spot. Similarly,
cleaning during status would turn a read path into a filesystem mutation
and introduce races with a running Hook.

Finally, -B is not a security sandbox: Hook code can still explicitly write
files. Such unexpected writes remain visible to the unchanged full-tree
digest, which is the intended behavior.

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

Labels

component:anolisa src/anolisa component:sec-core src/agent-sec-core/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[anolisa] sec-core Hook 生成 __pycache__ 导致 bundle drift 误报

1 participant