a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
81 lines
2.4 KiB
Go
81 lines
2.4 KiB
Go
package contracts
|
|
|
|
import "testing"
|
|
|
|
// consumerPaths returns the set of HTTP consumer route paths the extractor
|
|
// minted for src, keyed by path.
|
|
func consumerPaths(t *testing.T, file string, src string) map[string]bool {
|
|
t.Helper()
|
|
nodes := makeNodes(file, []struct {
|
|
name string
|
|
start, end int
|
|
}{
|
|
{"caller", 1, 20},
|
|
})
|
|
ext := &HTTPExtractor{}
|
|
out := ext.Extract(file, []byte(src), nodes, nil)
|
|
got := map[string]bool{}
|
|
for _, c := range out {
|
|
if c.Type == ContractHTTP && c.Role == RoleConsumer {
|
|
if p, _ := c.Meta["path"].(string); p != "" {
|
|
got[p] = true
|
|
}
|
|
}
|
|
}
|
|
return got
|
|
}
|
|
|
|
// TestConsumerGuard_StaticAssetRejected pins the consumer-pass acceptance: a
|
|
// static-asset fetch is not an API consumer, while a real API fetch still is.
|
|
func TestConsumerGuard_StaticAssetRejected(t *testing.T) {
|
|
got := consumerPaths(t, "app.js", `
|
|
async function load() {
|
|
await fetch('/static/app.js');
|
|
await fetch('/api/x');
|
|
}
|
|
`)
|
|
if got["/static/app.js"] {
|
|
t.Errorf("static asset /static/app.js minted a consumer contract")
|
|
}
|
|
if !got["/api/x"] {
|
|
t.Errorf("expected /api/x to still mint a consumer contract; got %v", got)
|
|
}
|
|
}
|
|
|
|
// TestConsumerGuard_FilesystemConfigRejected covers the filesystem/config
|
|
// false-positive: a rooted config-file literal is not an HTTP consumer.
|
|
func TestConsumerGuard_FilesystemConfigRejected(t *testing.T) {
|
|
got := consumerPaths(t, "app.js", `
|
|
async function load() {
|
|
await fetch('/etc/app.conf');
|
|
await fetch('/var/log/app.log');
|
|
await fetch('/api/users');
|
|
}
|
|
`)
|
|
if got["/etc/app.conf"] {
|
|
t.Errorf("config path /etc/app.conf minted a consumer contract")
|
|
}
|
|
if got["/var/log/app.log"] {
|
|
t.Errorf("log path /var/log/app.log minted a consumer contract")
|
|
}
|
|
if !got["/api/users"] {
|
|
t.Errorf("expected /api/users to still mint a consumer contract; got %v", got)
|
|
}
|
|
}
|
|
|
|
// TestConsumerGuard_RelativeAndURLPreserved guards against over-filtering:
|
|
// non-rooted (relative/template) and absolute-URL consumers keep their
|
|
// existing behaviour and are not dropped by the rooted-path gate.
|
|
func TestConsumerGuard_AbsoluteURLPreserved(t *testing.T) {
|
|
got := consumerPaths(t, "client.py", `
|
|
import requests
|
|
def call():
|
|
requests.get("http://service/api/users")
|
|
`)
|
|
// The literal does not start with "/" (it starts with "http://"), so the
|
|
// rooted-path gate never touches it — the consumer must survive.
|
|
if len(got) == 0 {
|
|
t.Errorf("absolute-URL consumer should still mint a contract; got none")
|
|
}
|
|
}
|