detect GitHub repos in Brain capture and offer post-clone scan/study agents - #3628
Merged
Conversation
…agents
A bare GitHub repo URL captured in the Brain was already saved as a link and
cloned in the background, but nothing said so and nothing followed. Both capture
boxes now name the owner/repo that will be cloned and offer two sticky opt-ins:
- Scan for malware — the same read-only /do:scan audit as the Links tab's Scan
button, now sharing one task shape via services/repoIntake.js
- Study for PortOS ideas — a repo-study run that reads the clone for ideas
worth adopting and files them into the configured work tracker
Both dispatch only after the clone succeeds, and the request is persisted on the
link so a Clone/Retry after a failed clone still honors it.
The study prompt is clean-room by contract: never execute anything from the
clone, never edit it, read its LICENSE first, describe techniques rather than
copying code.
Two supporting changes at the shared layer:
- declaresNoCommitCriterion now also accepts the `workTracker` marker a
tracker-filing dispatch stamps, so a one-off run reaches the no-commit
exemption without claiming a scheduled task type — which would have enrolled
it in taskSchedule's per-type failure ledger and auto-parked a type no
schedule owns.
- resolveTrackerFilingBlock moves from cosTaskGenerator.js into
lib/workTracker.js, next to the wording table it reads, so all three
tracker-filing dispatch paths share one resolution instead of copying it.
Also fixes the Links-tab Scan button recording nothing on the link, which left
it re-arming into a duplicate-task 409 after a reload.
parseGitHubUrl matched both segments as `[^/]+`, so a crafted URL parsed to a dot segment: `https://github.com/../evil` yielded owner `..`, and githubCloner's `join(reposDir, owner, repo)` then resolved OUTSIDE the managed clone root (`/data/repos/../evil` → `/data/evil`). `github.com/foo/..` collapsed to the repos root itself. That localPath is persisted on the link and handed to an agent as the directory to scan or study, so a bad parse aims a run at an arbitrary directory. Anchor both segments to the character sets GitHub actually allows (owner: a login, alphanumeric with internal hyphens; repo: alphanumerics plus _ . -), reject the `.`/`..` repo names that class still admits, and anchor the host so `https://evil.example.com/github.com/o/r` is no longer read as a GitHub repo. Found by the pre-PR review gate. Pre-existing in githubCloner.js, but this branch promotes that parser to the shared lib/ rule and adds the agent-facing consumer, so it lands here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A bare GitHub repo URL captured in the Brain was already saved as a link and cloned in the background — but nothing said so, and nothing followed. Both capture boxes (the Quick Capture dashboard widget and Brain → Inbox) now recognize a repo URL, name the
owner/repothat will be cloned, and offer two opt-in checkboxes:/do:scanaudit as the Links tab's Scan button, ending in a CLEAN / CAUTION / DANGEROUS report you open from the link.repo-studyagent that reads the clone for implementation ideas worth adopting and files the good ones into whichever tracker this install is configured for (GitHub issues / PLAN.md / GitLab / JIRA).Both preferences stick, so "always scan what I clone" is a one-time tick. The agents dispatch only after the clone succeeds, and the request is persisted on the link — so a Clone/Retry after a failed clone still honors it. Re-pasting a repo you already saved queues nothing, since there is no new clone to read.
The study prompt is clean-room by contract: never execute anything from the clone, never edit it, read its LICENSE first, and describe techniques in its own words rather than copying code.
Supporting changes
declaresNoCommitCriterionnow also accepts theworkTrackermarker a tracker-filing dispatch already stamps. Previously the only way for a one-off tracker-filing run to earn the no-commit exemption was to claim a scheduled task type — which would also enroll it intaskSchedule's per-type consecutive-failure ledger and eventually auto-park (and notify about) a "type" no schedule owns.resolveTrackerFilingBlockmoves fromcosTaskGenerator.jsintolib/workTracker.js, next to the wording table it reads, so all three tracker-filing dispatch paths share one resolution instead of a third hand-copy.parseGitHubUrlmoves toserver/lib/githubRepoUrl.jswith a client mirror (parity pinned by a test), so the capture boxes predict "this will be cloned" exactly as the server decides it.[^/]+, sohttps://github.com/../evilparsed to owner..andjoin(reposDir, owner, repo)resolved outside the managed clone root — a path since handed to an agent as its scan directory. Segments are now anchored to GitHub's real character sets, dot segments rejected, and the host anchored soevil.example.com/github.com/o/ris no longer read as a repo.Test plan
cd server && npm test— 1200 files pass; the 51 failing files are the pre-existing Postgres-backed suites (no DB provisioned in this worktree), byte-identical to the pre-change baseline.cd client && npm test— 558 files / 6590 tests pass.biome lintclean,npm run buildclean.server/lib/githubRepoUrl.test.js— parse shapes, deep links,.gitstripping, and the path-safety cases (dot segments, percent-encoding, foreign hosts, clone-path containment).server/lib/githubRepoUrl.mirror.test.js— server↔client parity; verified it fails on a planted divergence.server/lib/repoIntakeActions.{test,mirror.test}.js— normalizer semantics (null on nothing-ticked, literal-trueonly, unknown keys dropped) and client↔server action-key parity.server/services/repoIntake.test.js— task shape for both actions, tracker resolution across github/plan/origin-failure, the link patch each returns, and one action failing without taking the other down.server/services/taskTypeHooks.noCommit.test.js— the generalized no-commit gate, including that the flag alone does not exempt andautois not a resolved tracker.server/services/brain.test.js— intake threading: stored on the link, normalized, dropped for non-GitHub URLs, and not re-queued on a re-paste.client/src/components/QuickBrainCapture.test.jsx— options appear only for a bare repo URL (not for prose-wrapped or non-repo github.com URLs), stick across mounts, and are omitted when the text is no longer a repo URL at submit time.