Skip to content

Strange capacity misreporting by capslock and capslock-git-diff #295

@deltamualpha

Description

@deltamualpha

I'm still trying to narrow this down to a reproducer, but figured I'd file an issue just to get started.

I've been running capslock-git-diff on internal pipelines at work, and by and large it doesn't report changes -- which is to be expected, most libraries don't change capabilities that often. However, today, I upgraded one internal service to start using 1.26, and ran go fix as part of that. Among other changes, it updated two usages of a range over strings.Split to instead range over strings.SplitSeq; a straightforward enough change.

However, capslock-git-diff reports:

❯ capslock-git-diff -v master .
2026/02/25 23:19:27 analyzing at revision "master"
2026/02/25 23:19:27 running git with args ["rev-parse" "--git-dir"]
2026/02/25 23:19:27 git directory: ".git"
2026/02/25 23:19:27 running git with args ["rev-parse" "--show-prefix"]
2026/02/25 23:19:27 current path in repository: ""
2026/02/25 23:19:27 running git with args ["clone" "--shared" "--no-checkout" "--" ".git" "/var/folders/vl/k9dw37lx0x5bbtzj98plsqbh0000gp/T/2381433021"]
Cloning into '/var/folders/vl/k9dw37lx0x5bbtzj98plsqbh0000gp/T/2381433021'...
done.
2026/02/25 23:19:27 switched to directory "/var/folders/vl/k9dw37lx0x5bbtzj98plsqbh0000gp/T/2381433021"
2026/02/25 23:19:27 running git with args ["checkout" "master" "--"]
Switched to a new branch 'master'
2026/02/25 23:19:27 switched to directory "/var/folders/vl/k9dw37lx0x5bbtzj98plsqbh0000gp/T/2381433021"
2026/02/25 23:19:27 running capslock with args ["-packages=./..." "-output=json" "-granularity=intermediate" "-capabilities=-UNANALYZED"]
2026/02/25 23:19:29 capslock returned "{\n\t\"capabilityInfo\":  [\n\t\t{\n\t\t\t\"packageName\":  \"bufio\",\n\t\t\t\"capabilityName\":  \"FILES\",\n\t\t\t\"capabilit..."
2026/02/25 23:19:29 parsed CapabilityInfoList with 1032 entries
2026/02/25 23:19:29 returned to working directory "/Users/pwd"
2026/02/25 23:19:29 analyzing at revision "."
2026/02/25 23:19:29 running capslock with args ["-packages=./..." "-output=json" "-granularity=intermediate" "-capabilities=-UNANALYZED"]
2026/02/25 23:19:32 capslock returned "{\n\t\"capabilityInfo\":  [\n\t\t{\n\t\t\t\"packageName\":  \"bufio\",\n\t\t\t\"capabilityName\":  \"FILES\",\n\t\t\t\"capabilit..."
2026/02/25 23:19:32 parsed CapabilityInfoList with 1033 entries
Comparing capabilities in "./..." between revisions "master" and "."


Added 1 new use of existing capability:
          NETWORK:    Access to the network

New packages in call paths to capability NETWORK:

Package internal.service.snip/model has capability NETWORK:
               internal.service.snip/model.ParseLicenses
               strings.splitSeq$1
iter.go:41:14  net/http.containsDotDot$1

I've gone and looked at line 41 of iter.go; it certainly does not appear to have any reference to http.containsDotDot in it (which is unexported anyway).

More curiously, if I run capslock directly against the fix'd code:

❯ capslock -packages internal.service.snip/model
Capslock is an experimental tool for static analysis of Go packages.
Share feedback and file bugs at https://github.com/google/capslock.
For additional debugging signals, use verbose mode with -output=verbose
To get machine-readable full analysis output, use -output=json

Analyzed packages:
  internal.library.package v0.0.0-snip
Capslock found no capabilities in this package.

Capslock doesn't think that the package in question has any capabilities at all.

When I run capslock -granularity=intermediate -packages=./... -output=json on both commits and compare the output, the only change is indeed the new appearance of

{
	"packageName":  "model",
	"capabilityName":  "NETWORK",
	"capability":  "CAPABILITY_NETWORK",
	"path":  [
		{
			"name":  "internal.service.snip/model.ParseLicenses",
			"package":  "internal.service.snip/model"
		},
		{
			"name":  "strings.splitSeq$1",
			"package":  "strings"
		},
		{
			"name":  "net/http.containsDotDot$1",
			"site":  {
				"filename":  "iter.go",
				"line":  "41",
				"column":  "14"
			},
			"package":  "net/http"
		}
	],
	"packageDir":  "internal.service.snip/model"
}

So somehow, capslock is convinced that the strings package imports and uses a private function from net/http.

I've tried a narrow reproducer of a git repo that just has

func main() {
	// doesn't actually matter what we do
	for _, s := range strings.Split("a,b,c", ",") {
		fmt.Println(s)
	}
}

and

func main() {
	// doesn't actually matter what we do
	for s := range strings.SplitSeq("a,b,c", ",") {
		fmt.Println(s)
	}
}

as the two commits, but that doesn't seem to trigger it.

Do you have any further guidance on how I can debug this strange behavior?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions