Skip to content

fix(periodic-report): reject native, UNC and escaped Windows private paths in the run core - #6362

Closed
JasonBuildAI wants to merge 2 commits into
loopx-project:mainfrom
JasonBuildAI:codex/agent-y3h7-topic
Closed

JasonBuildAI wants to merge 2 commits into
loopx-project:mainfrom
JasonBuildAI:codex/agent-y3h7-topic

Conversation

@JasonBuildAI

@JasonBuildAI JasonBuildAI commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Goal And Delivered Outcome

Author Declaration

  • Written by: model_agent (OpenAI Codex CLI agent, GPT-5-family model), directed by the human operator who owns the account; the operator set the task constraints and reviews the change before submission.
  • AI disclosure: this change was drafted with AI assistance at my request, under my direction and review.

Implemented against

Criterion (spec clause) Disposition Symbol / path Test or command
The core rejects private paths in run requests implemented loopx/capabilities/periodic_report/core.py (_LOCAL_PATH_SURFACE_PATTERN, _reject_raw_keys) tests/capabilities/test_periodic_report.py::test_private_path_spelling_variants_are_rejected
Refusal is visible on the caller entrypoint implemented loopx/capabilities/periodic_report/cli.py (compose-run) ...::test_periodic_report_cli_refuses_native_windows_private_path
Public values keep their verdict (no loosening of accepted input) implemented same rule ...::test_public_windows_paths_and_urls_stay_accepted
A malformed request fails fast instead of stalling the scan implemented same rule ...::test_long_separator_run_stays_bounded
  • Self-check before submission: reproduced the defect before the change through the run builder and the CLI with a 13-case probe matrix (6 native/UNC/rooted/escaped spellings wrongly accepted; POSIX and mixed spellings already refused; public drive/UNC paths, https:// URLs and clean tokens already accepted), then re-ran the same probe after the change (0 mismatches). An independent review of the first head measured that the initial leading separator-run quantifier could rescan long non-root runs quadratically (32,768 backslashes: 9.1 s through the builder, and the raw-value scan runs before field length validation); the lead is now a single separator, acceptance-identical (a 200,000-case fuzz over separator/root-heavy strings shows 0 verdict differences between the two lead forms and 0 loosening against the pre-change rule) and linear (65,536 separators: 0.0013 s; the same request now fails the length check in 0.016 s), pinned by a bounded regression test. Also ran the changed test file, the periodic-report family with a baseline/head failure-set comparison, CI-scope ruff, mypy, the semantic inventory advisory, loopx check, and loopx canary premerge. Deliberately left out: the other POSIX-only copies of this pattern (loopx/chat_action_store.py, loopx/registry.py) and a migration onto loopx/public_safe_text.py, because the shared owner's lookbehind deliberately drops spellings this surface refuses today (x/Users/..., //Users/...) and its drive arm refuses paths outside the private roots; consolidation is owned by [Architecture]: two modules both claim to own "private-looking text" #5136, not by this fix.

Scope And Continuation

Validation

  • Tested revision: 1698eb5 (first head be2c434 carried the same core fix; the perf hardening above was added after independent review)
  • Run state: finished
  • Input classes: synthetic
Check kind Result Public-safe evidence / limitation
static passed python -m ruff check tests loopx/canary loopx/control_plane loopx/domain_packs loopx/presentation (CI scope): all checks passed; python -m ruff check loopx/capabilities/periodic_report/core.py tests/capabilities/test_periodic_report.py: all checks passed
unit passed python -m pytest -q tests/capabilities/test_periodic_report.py: 29 passed (includes the four new tests); adjacent subset (_adapters, _profile, _audience, _triggers, _bindings, CLI capability chain): 72 passed
regression_parity passed separator-run lead forms accept the same strings: 200,000-case fuzz, 0 verdict differences and 0 loosening vs the pre-change rule; on a crafted 32,768-separator reference the scan time drops from 9.12 s to 0.016 s, and the pattern scales linearly to 65,536 separators in 0.0013 s.
regression_parity passed periodic-report family tests (21 files: 228 passed, 80 failed pre-existing host failures, all ModuleNotFoundError: No module named 'tzdata' on this Windows checkout without the tzdata wheel); baseline vs head run of the five failing files gives byte-identical sorted FAILED sets (80 vs 80) and identical pass counts (34), so no verdict changed
real_entrypoint passed source-checkout CLI before the change: native drive spelling → exit 0, "ok": true; after: exit 1, periodic_report_error_v0 with "contains a private path or credential-like value"; POSIX control case refused in both runs
manual passed loopx check --scan-path <both changed files>: public boundary scan clean, errors 0, warnings 0; loopx canary premerge --from-git-diff: passed, merge gate passed, self-merge not authorized; python scripts/generate_semantic_inventory.py --changed-from HEAD: no supported new vocabulary carriers; python -m mypy: one pre-existing Windows-only os.WNOHANG error in the untouched loopx/control_plane/effect_runtime.py, changed files are outside the checked list
  • Coverage and gaps: the changed rule is exercised end-to-end through the run builder and the CLI, with negative controls for paths outside the private roots and for URLs, plus a bounded long-separator regression test. Untested: Windows extended-length \\?\ prefixes and drive-relative C:Users\... spellings, which ordinary hosts do not produce, and the same-pattern copies listed above.

Frontend / Visual Evidence

  • UI impact: none
  • Before: N/A
  • After: N/A
  • States and viewports shown: N/A
  • Source data: none
  • Attention review: N/A — no UI surface changed.
  • Craft review: N/A — no UI surface changed.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring (no functional changes)
  • Documentation update
  • Test update

LoopX Area

  • Control plane (goals, todos, quota, scheduler, registry, runtime)
  • Benchmark boundary (adapters, runners, verifiers, scoring, evidence)
  • Capability or extension (providers, adapters, skills)
  • Public docs or presentation surface (README, protocols, dashboard)
  • Build, packaging, installer, or CI
  • Host or runtime integration

Technical Direction

  • Direction / acceptance reference, when applicable: N/A — bounded capability repair against a documented protocol contract.

Shared-authority RFC fixture impact

  • Production-scale fixture schema: N/A
  • Semantic dimensions changed, or reviewed no-impact rationale: N/A
  • Provider conformance arms run: N/A
  • Read-only legacy/file/PostgreSQL three-arm rehearsal (required for promotion, runtime-routing, or compatibility-projection changes): N/A

Boundary Checklist

  • Neither the diff nor this PR body/comments/attachments disclose private state, credentials, raw traces or verifier output, internal links, or local machine paths (including .loopx/, .codex/goals/, and live ACTIVE_GOAL_STATE.md).
  • I did not duplicate maintainer-owned benchmark work unless a maintainer split out a public issue for it.
  • I kept the change scoped to the linked issue/task.
  • I completed the visual evidence section for UI changes, or marked UI impact none.
  • Every commit includes a DCO Signed-off-by trailer (git commit -s).

…paths in the run core

The run-request core is the gate for the periodic-report capability, and its
private-path rule recognized only the POSIX spelling of the private roots.
The same path written the way a Windows host or a serialized string spells
it (C:\Users\<name>\..., \\server\Users\<name>\..., the rooted
\Users\<name>\..., and the escaped C:\\Users\\<name>\\...) was accepted into
the run identity and echoed in the composed packet while the POSIX spelling
was refused, contrary to the protocol's documented "rejects ... private
paths" contract.

Match a separator run at every junction of the same private roots, the class
the boundary scan (loopx-project#6161) and the public-safe-outbound scanner (loopx-project#6223)
already reject. Paths outside the private roots, and URLs, stay acceptable;
the native, UNC, rooted and escaped spellings plus the CLI refusal path are
pinned by tests.

Closes loopx-project#6358

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
JasonBuildAI added a commit to JasonBuildAI/loopx that referenced this pull request Oct 13, 2026
…#6362

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
…r runs

An independent review of the first commit measured the leading separator-run
quantifier retrying every start position inside a long non-root run, and the
raw-value scan runs before field length validation, so a request carrying
tens of kilobytes of separators stalled compose-run (32,768 backslashes:
9.1 s through the builder). A single leading separator is
acceptance-identical (the engine can start at the last separator of a run;
200,000-case fuzz: 0 verdict differences and 0 loosening) and scans linearly
(65,536 separators: 0.0013 s). A bounded regression test pins it, and the
Windows spellings of the inner var/folders and private/tmp junctions get
explicit cases.

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
@mergify

mergify Bot commented Oct 13, 2026

Copy link
Copy Markdown

This pull request has merge conflicts with main and cannot be merged
until they are resolved. Please rebase or merge the base branch, @JasonBuildAI.

Choose the remote for the base repository, not an out-of-date fork.
For a fork clone, first inspect git remote -v; upstream must point
to https://github.com/loopx-project/loopx.git. If it is absent, add it
with git remote add upstream https://github.com/loopx-project/loopx.git.
Then run:

git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEAD

For a same-repository clone whose origin points to
https://github.com/loopx-project/loopx.git, use origin instead of
upstream for fetch/rebase. If you prefer merging the base, use
git merge <base-remote>/main and push normally.

Keep the DCO Signed-off-by trailer on every commit when you rebase.
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Oct 13, 2026
@JasonBuildAI

Copy link
Copy Markdown
Contributor Author

Closing as superseded: #6363 (merged at 09:26Z, closes #6358) already landed this fix on main, and this branch no longer carries a delta to rebase.

Verified before closing:

  • The rule now on main is AST-identical to this head's _LOCAL_PATH_SURFACE_PATTERN, including the single-leading-separator linear form and its rationale:
(?<!<)[\\/](?:Users|Volumes|home|var[\\/]+folders|tmp|private[\\/]+tmp)[\\/]+[^\s`'"<>]+

The only difference is where the explanatory comment sits (above the assignment here, inside the re.compile(...) call on main).

  • The merged test suite is a superset of this branch's four tests: six roots × eight separator spellings (POSIX, drive, UNC, rooted, mixed, escaped drive/UNC, mixed runs), the recursive request-gate fields (profile_ref, artifact_ref, receipt_ref), CLI refusal through file and stdin with recovery to a successful run, unchanged successful run/sink identity, Lark send-callback ordering, and the bounded long-separator run reaching the reference-length error.
  • The merge conflict is the mechanical consequence of both branches editing the same re.compile lines in loopx/capabilities/periodic_report/core.py (the test file auto-merges). Resolving it would only recreate code already on main, so no rebase is pushed.

No action requested. If something specific from this branch should be kept (for example its separate wall-clock bounded test), say so and I can open a small follow-up against current main.

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

Labels

needs-rebase Mergify: the pull request has merge conflicts with its base branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: periodic-report run core rejects POSIX private paths but not the native Windows spelling

1 participant