Skip to content

id(app_manifest): discover_apps follows symlinked app folders — an app outside the apps root registers as a local app #4067

Closed

Description

@dezren39

Problem

discover_apps (pkgs/id/src/app_manifest.rs:152-200, main @ df3f836) follows symlinks when scanning the apps root, so an "app folder" that is actually a symlink to a directory outside the apps root is registered as a local app:

  • candidate folders are filtered with path.is_dir() (app_manifest.rs:182), which follows symlinks;
  • the manifest is loaded via folder.join("app.toml") + is_file() (:186-190), also following symlinks;
  • the folder name used for the id-match check is the symlink's name (:187-190), so an external folder registers whenever its manifest id matches the symlink's name.

Verified this run with a probe crate on the laptop compiling a sha256-identical copy of app_manifest.rs from main df3f836 (probe: ~/git/fleet/probe-appmanifest-20261101 on the laptop; result confirmed in two independent runs): apps root containing two real folders plus linked-app → symlink to a directory outside the root holding a valid app.toml with id = "linked-app" → discover_apps returns apps=["app-one", "app-two", "linked-app"], errors=0.

Why it matters

The app-folder model (design doc/2026-10-29T12-50-36Z_design_id_app_packaging §2/§3) treats the folder as the unit of trust: everything is relative to it, its name must equal the app id, and discovery "registers the app". A symlink hop silently breaks that unit — the registered app's program.source, [resources].files, and templates all resolve in a directory the operator never placed under the apps root, and a later folder-copy/backup of the root captures a dangling or different app than the one registered. The fleet hit this exact shape in pm-dispatch and fixed it as #3999 (symlink confinement, merged today); discovery has no equivalent stance — it does not even record that a folder was a symlink.

Acceptance criteria

  • Discovery takes an explicit stance on symlinked app folders: either skip them with a named per-app error in AppDiscovery.errors, or resolve and register them only when the resolved target stays inside the apps root — the chosen rule is documented in the module docs.
  • A unit test builds an apps root with a symlinked folder pointing outside it and asserts the chosen behavior (not silently registered as a plain local app).
  • Real (non-symlink) app folders, including the checked-in tic-tac-toe app, discover exactly as today.
  • Symlinks inside a real app folder (e.g. a linked program/ entry) are either covered by the same rule or explicitly called out as out of scope in the module docs.

Activity

  1. added
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/normalOrdinary priority (the default)
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    kind/securitySecurity issue or security hardening
    effort/sSmall: expected under 2 hours of work
    on Nov 1, 2026
  2. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator job (new-issue-creator-code-a)
    • Session / ID: new-issue-creator-code-a run 2026-11-01 17:05 CST
    • Found via: filed this run
    • Task: Filed this issue — discover_apps follows symlinked app folders and registers an app whose real folder is outside the apps root; verified twice by a laptop probe crate against a sha256-identical copy of app_manifest.rs @ df3f836 (linked-app discovered, errors=0). Note: the first probe readout shown to me this run truncated this line and I initially recorded the hypothesis as not reproduced — the complete output and an independent re-run both confirm it reproduces; bookkeeping corrected this run.
    • Expectation: effort/s — triage confirms on current main, picks the symlink stance per the acceptance criteria, routes the fix
    • Links: queue packet enqueued this run (repo-health-code-pilot); related confinement fix: issue pm-dispatch(sessions): directory confinement is lexical only — committed symlinks in the clone (nixpkgs, worktrees) escape it (residual of #2903) #3999 (pm-dispatch)
  3. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · adopted as Phase 1 of design #4202

  4. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor
  5. 11 remaining items

  6. added
    task/fixhas a fix task queued
    queue/queuedHas a queued task in the Supabase agent queue
    status/readyTriaged and ready for queued implementation work
    on Nov 2, 2026
  7. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  8. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    status/readyTriaged and ready for queued implementation work
    on Nov 2, 2026
  9. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

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

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/securitySecurity issue or security hardeningpriority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobsource/design-architectFiled by the repo design architect jobtask/fixhas a fix task queuedtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions