Skip to content

fix(appstore): refuse downgrade install (lower app_version) (PILOT-105)#3

Merged
TeoSlayer merged 1 commit into
mainfrom
openclaw/pilot-105-20260528-073912
May 28, 2026
Merged

fix(appstore): refuse downgrade install (lower app_version) (PILOT-105)#3
TeoSlayer merged 1 commit into
mainfrom
openclaw/pilot-105-20260528-073912

Conversation

@matthew-pilot
Copy link
Copy Markdown
Collaborator

What

Adds downgrade protection to the app-store supervisor: refuses to install an app whose manifest app_version is lower than a currently-registered entry for the same app ID.

Why

The install flow had no monotonic version check — re-installing an older app_version on top of a newer one succeeded silently. This is a classic rollback-attack vector: a CVE patched in v1.2 can be re-introduced by reinstalling v1.1.

Changes

  • compareVersions() — basic semver comparison (MAJOR.MINOR.PATCH with optional -PRERELEASE)
  • registerInstalled() — refuses to replace in-memory entry with lower app_version (startup path)
  • rescanForNew() — detects on-disk manifest version changes for already-known apps; refuses downgrades; upgrades are accepted and trigger a clean restart of the supervise goroutine
  • Audit: downgrade-refused events written to supervisor.log for forensics

Scope: 1 production file (+~80 LoC), 1 test file (+~220 LoC), no denied paths.

Verification

go build ./...   # clean
go vet ./...     # clean
go test ./...    # ok (all 5 packages, existing + 7 new downgrade tests)

Note

This is best-effort defense-in-depth. Full protection requires signed manifests (PILOT-98, the #5 dependency) to prevent an attacker from forging the app_version field alongside the binary. Until then, this prevents accidental operator downgrades and raises the bar for on-disk tampering (audit log + log warning trail).

Closes PILOT-105

The install flow had no monotonic version check — re-installing an
older app_version on top of a newer one succeeded silently. This is a
classic rollback-attack vector: a CVE patched in v1.2 can be
re-introduced by reinstalling v1.1.

This adds:
- compareVersions() — basic semver comparison (MAJOR.MINOR.PATCH[-PRE])
- registerInstalled() now refuses to replace an in-memory entry with
  a lower app_version for the same app ID (startup path).
- rescanForNew() now detects on-disk manifest version changes for
  already-known apps and refuses downgrades; upgrades are accepted
  and trigger a clean restart of the supervise goroutine.
- Audit events (downgrade-refused) in the supervisor log for forensics.

The check is best-effort defense-in-depth — full protection requires
signed manifests (PILOT-98, the #5 dependency) to prevent an attacker
from forging the app_version field alongside the binary.

Closes PILOT-105
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

❌ Patch coverage is 88.88889% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
plugin/appstore/supervisor.go 88.88% 5 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@TeoSlayer TeoSlayer merged commit 8037462 into main May 28, 2026
2 of 3 checks passed
@TeoSlayer TeoSlayer deleted the openclaw/pilot-105-20260528-073912 branch May 28, 2026 16:57
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.

2 participants