feat: scan any public repository by name, anonymously - #67
Merged
Conversation
Type owner/repository, get per-file deterministic findings. The demo asked a visitor to paste YAML; this asks for a name and nothing else — the same fetch path the 564-workflow field scan already proved, now a product surface. The quota is the design constraint. GitHub allows 60 unauthenticated API calls per hour per source address, shared by every visitor this host serves. So the directory listing is the only metered call — file bodies come from raw.githubusercontent.com, which is not counted — and every outcome is cached, failures included: a repository that does not exist is an answer worth remembering, or one stranger retrying a typo drains the hour for everyone. Exhaustion is reported as exhaustion, not as a missing repository. The trust boundary widens by one stated step and no more. The middleware's justification for anonymous access was "no outbound call"; this endpoint makes one, so the amended boundary is written down where the old one was: only api.github.com and raw.githubusercontent.com, no credential attached, read-only by construction. Owner and repository names are validated against GitHub's own shape before they touch a URL. Private repositories cannot appear — an anonymous request cannot see them, which is why this needs no allowlist while the GitHub App integration does. Limits are counted, not silent: files beyond the 30-file cap or the 100k character cap are reported as skipped. The UI grows a third tab beside Simulation and GitHub Sandbox. Its tests caught a real race on the way in: the scenario-loading effect's promise could resolve after a tab switch and wipe newer results via resetResults(). The effect now cancels on cleanup, so a stale response is a no-op. 218 backend tests and 22 frontend tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| RuleDiscovery.All(), | ||
| new NeverResolvesActionReferenceResolver(), | ||
| new GitHubOptions())), | ||
| new MemoryCache(new MemoryCacheOptions()), |
Comment on lines
+201
to
+204
| HttpResponseMessage response = new(fake.ListingStatus) | ||
| { | ||
| Content = new StringContent(fake._listingBody, Encoding.UTF8, "application/json") | ||
| }; |
|
|
||
| fake.RawRequests.Add(url); | ||
| return Task.FromResult(fake._raw.TryGetValue(url, out string? content) | ||
| ? new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(content) } |
| fake.RawRequests.Add(url); | ||
| return Task.FromResult(fake._raw.TryGetValue(url, out string? content) | ||
| ? new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(content) } | ||
| : new HttpResponseMessage(HttpStatusCode.NotFound)); |
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.
Type
owner/repository— or paste a github.com URL — and get per-file deterministic findings. No signup, no token, no paste. The same fetch path the 564-workflow field scan proved, now a product surface.Design constraint: the quota
GitHub allows 60 unauthenticated API calls/hour per source address, shared by every visitor this host serves.
raw.githubusercontent.com, which is not countedTrust boundary, amended in writing
The auth middleware justified anonymous access with "no outbound call." This endpoint makes one, so the boundary is restated where the old one was: api.github.com and raw.githubusercontent.com only, no credential attached, read-only by construction. Names are validated against GitHub's own shape before they touch a URL (pinned by test:
../etcnever becomes a request). Private repositories cannot appear — anonymous requests cannot see them, which is why this needs no allowlist while/api/githubdoes.Caps are counted, not silent: files beyond 30 per repo or 100k characters are reported as
skippedFiles.The race the new tests caught
The scenario-loading effect's promise could resolve after a tab switch and wipe newer results via
resetResults()— a real clobber, milliseconds wide in production, deterministic under test. The effect now cancels on cleanup; a stale response is a no-op.Verification