Files
sift/docs/plans/p6/_files/guard/precheck_test.go
T
kyleandClaude Opus 5.5 218eaa8b53 P6 plan, owner review folded in: caller travels with the job, 1–2 round-robin workers, busy retry, summary reads, local-only tests, 8 tasks
Kyle: "Fold them into the plan." What changed and why is in docs/P6.md §14. In short:
- FetchFunc gets the *auth.Caller (the given api test could not compile, and a fetch with no
  caller would bypass per-caller gates, the pool limit, the cache class and redaction);
- store reads never bump the lease (task 01's rule contradicted its own test); the API Touches;
- 1-2 workers round-robin over live jobs, so a batch holds at most 2 of the 8 global slots;
  busy targets retry in the runner (bounded); batch PDFs use their own pool name; 210 s deadline;
- the api tests no longer touch the network or race the cancel; private names via fake DNS;
- failed/cancelled jobs are not reused; GET is a summary plus GET …/{idx} and ?results=1;
- the 256 KiB body cap moves into ServeHTTP; a fixed metrics label for /v1/batch/{id};
- guard.PreCheck (given verbatim) replaces guard.ParseURL at submit;
- 5 tasks became 8 (refactor, wiring, endpoints and serve/config split), each restating the
  APIs it uses; every given test compiles against a panic-only skeleton and fails on it.

P6 starts after P5c: task 04 moves v1fetch, which P5b changes. Recheck the api given tests
against post-P5c master before the run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 17:55:50 -07:00

54 lines
1.5 KiB
Go

package guard
// P6 task 05: PreCheck is CheckURL without name resolution, for refusing a batch target at
// submit time.
import (
"context"
"net/netip"
"strings"
"testing"
)
func TestPreCheck(t *testing.T) {
g := &Guard{
AllowPrefixes: []netip.Prefix{netip.MustParsePrefix("127.0.0.0/8")},
LookupIP: func(context.Context, string) ([]netip.Addr, error) {
t.Fatal("PreCheck resolved a name")
return nil, nil
},
}
for _, u := range []string{
"https://example.com/",
"http://example.com/path?q=1",
"https://internal.test/", // a name: not resolved here, so not refused here
"http://127.0.0.1:8080/", // an allowed literal is exempt from the port rule, as in CheckURL
"http://93.184.215.14/", // a public literal
"http://0x7f000001:8080/", // inet_aton form of 127.0.0.1: an allowed literal
} {
if err := g.PreCheck(u); err != nil {
t.Errorf("PreCheck(%q) = %v, want nil", u, err)
}
}
for _, u := range []string{
"",
"ftp://example.com/",
"http://user@example.com/",
"http://example.com:8080/",
"http://10.0.0.5/",
"http://169.254.169.254/latest/meta-data/",
"http://[::1]/",
"http://0x0a000005/", // inet_aton form of 10.0.0.5
} {
err := g.PreCheck(u)
if err == nil || !strings.HasPrefix(err.Error(), "blocked") {
t.Errorf("PreCheck(%q) = %v, want a blocked error", u, err)
}
}
// With no allow list, loopback is refused like any other private literal.
var zero Guard
if err := zero.PreCheck("http://127.0.0.1/"); err == nil {
t.Error("a zero Guard allowed loopback")
}
}