GithubHelp home page GithubHelp logo

capslock's Introduction

capslock

Capslock is a capability analysis CLI for Go packages that informs users of which privileged operations a given package can access. This works by classifying the capabilities of Go packages by following transitive calls to privileged standard library operations.

The recent increase in supply chain attacks targeting open source software has highlighted that third party dependencies should not be inherently trusted. Capabilities indicate what permissions a package has access to, and can be used in conjunction with other security signals to indicate which code requires additional scrutiny before it can be considered trusted.

What are capabilities?

Current security analysis focuses a lot on identifying vulnerabilities in packages -- an important goal given the rate of new CVEs being identified. To complement this analysis, we are alerting on the capabilities of packages, meaning that we are identifying what permissions the package has access to via its transitive dependencies on standard library functions with privileged accesses.

This has many potential applications, from identifying the purpose of packages by looking at what capabilities they use, to directing security reviews to more privileged code paths, and even alerting on unexpected capability changes to stop potential supply chain threats before they can become an issue.

This is motivated by the Principle of Least Privilege -- the idea that access should be limited to the minimal set that is feasible and practical. We intend to apply this to software development to ensure that code can be scoped to the minimal set of capabilities that are required to perform its intended purpose.

To learn more about the capabilities in your dependencies, install Capslock

go install github.com/google/capslock/cmd/capslock@latest

You can then invoke Capslock by running capslock from the path of the packages you want to analyze.

Caveats

See the caveats file.

Contributing

See the contributing file.

Star History

Star History Chart

capslock's People

Contributors

adamdecaf avatar alexandear avatar breml avatar dependabot[bot] avatar djm-google avatar djmdjm avatar jessexoc avatar jessmcclintock avatar liamjm avatar mewmew avatar step-security-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

capslock's Issues

More info

When I run it, I see some info at the end , like:

CAPABILITY_ARBITRARY_EXECUTION: 3 references
CAPABILITY_FILES: 3 references
CAPABILITY_MODIFY_SYSTEM_STATE: 3 references
CAPABILITY_NETWORK: 3 references
CAPABILITY_OPERATING_SYSTEM: 2 references
CAPABILITY_READ_SYSTEM_STATE: 3 references
CAPABILITY_REFLECT: 3 references
CAPABILITY_SYSTEM_CALLS: 3 references
CAPABILITY_UNANALYZED: 3 references
CAPABILITY_UNSAFE_POINTER: 3 references

It would be nice if there was a flag (like -v or something) that would tell me more in detail about what the files and the positions of there references are.
Example
Like/my/dir/to/file.go:14:28
(this makes it easy to go to the position with just 1 click)

Ineffective check for len(packageNames) == 0

The check if len(packageNames) == 0 in

if len(packageNames) == 0 {
log.Fatal("No packages provided")
}

is ineffective for the following reasons:

  1. strings.Split does return a slice of length 1 for the empty string.
  2. if the flag -packages is used without additional argument, this is already covered by flag.Parse().
  3. even if the tool is called with capslock -packages "", we get the empty string and we are back to 1.

go method naming convention used by capslock is inconsistent with other popular tools

The naming convention that capslock uses for Go methods seems to be inconsistent with the conventions used by other tools (for example, debuggers, profilers, and so on).

Here is a toy program to demonstrate: playground link.

This program makes a series of calls into the text/template program from Go's standard library, at runtime most of the time is spent in that package. If I run this program through capslock, the convention used for reporting pointer Go methods is

(*<packagepath>.<type>).<methodname>

instead of the more commonly used

<packagepath>.(*<type>).<methodname>

Here is what I see from capslock:

$ capslock -output=graph > p.dot
$ fgrep '.walk"' *.dot | head -3
	"(*text/template.Template).execute" -> "(*text/template.state).walk"
	"(*text/template.state).walk" -> "(*text/template.state).errorf"
	"(*text/template.state).walk" -> "(*text/template.state).evalPipeline"
$

Examples of other tools that use the latter convention: pprof (profiler), delve (debugger). Specifics:

Profiler:

$ go run hotmeth.go 
$ ls *.p
prof.p
$ pprof prof.p
File: hotmeth
Type: cpu
Time: May 6, 2024 at 1:50pm (UTC)
Duration: 1.41s, Total samples = 1.40s (99.57%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top10
Showing nodes accounting for 520ms, 37.14% of 1400ms total
Showing top 10 nodes out of 210
      flat  flat%   sum%        cum   cum%
     120ms  8.57%  8.57%      340ms 24.29%  runtime.mallocgc
      80ms  5.71% 14.29%      280ms 20.00%  text/template/parse.(*lexer).nextItem
      50ms  3.57% 17.86%      560ms 40.00%  text/template/parse.(*Tree).textOrAction
      40ms  2.86% 20.71%       60ms  4.29%  reflect.(*structType).FieldByName
      40ms  2.86% 23.57%      120ms  8.57%  text/template.(*state).evalField
      40ms  2.86% 26.43%      300ms 21.43%  text/template.(*state).walk
      40ms  2.86% 29.29%      250ms 17.86%  text/template/parse.(*Tree).nextNonSpace
      40ms  2.86% 32.14%      740ms 52.86%  text/template/parse.(*Tree).parse
      40ms  2.86% 35.00%      130ms  9.29%  text/template/parse.(*Tree).peek
      30ms  2.14% 37.14%       50ms  3.57%  runtime.deductAssistCredit
(pprof)  

Debugger:

$ go build hotmeth.go
$ dlv debug .
Type 'help' for list of commands.
(dlv) b text/template.(*state).walk
Breakpoint 1 set at 0x586336 for text/template.(*state).walk() /w/ygo/src/text/template/exec.go:261
(dlv) c
> text/template.(*state).walk() /w/ygo/src/text/template/exec.go:261 (hits goroutine(1):1 total:1) (PC: 0x586336)
   256:		walkContinue = errors.New("continue")
   257:	)
   258:	
   259:	// Walk functions step through the major pieces of the template structure,
   260:	// generating output as they go.
=> 261:	func (s *state) walk(dot reflect.Value, node parse.Node) {
   262:		s.at(node)
   263:		switch node := node.(type) {
   264:		case *parse.ActionNode:
   265:			// Do not pop variables so they persist until next end.
   266:			// Also, if the action declares variables, don't print the result.
(dlv) b (*text/template.state).walk
Command failed: location "(*text/template.state).walk" not found
(dlv)

It would be nice if capslock could work the same way. Thanks.

Path elements in JSON output are not consistent between runs

I'm using capslock (probably in a way that is not intended) to identify module wide imports that are org-foreign. This is a cheap hack implemented here with obvious issues of false positives.

One of the extensions that I was interested in making was to use the path output to identify specific syscalls that are made when a CAPABILITY_SYSTEM_CALLS is fount. This requires examining the call path array in the capslock JSON output so that the AST of the package can be examined for the actual args of the sycall call. While looking into this, I found that the order of the input packages to capslock impacts on the output of the JSON and the final sites identified in the path are not stable between runs (the summary output and caps counts are stable).

To demonstrate this here are example runs of capslock with a post processor available here https://play.golang.com/p/JoC-mB_NZYA (built as an executable syscalls). The tests were run in packetbeat/protos directory of http://github.com/elastic/beats.

Different packages order (note missing unix.CmsgSpace callsite is consistent between runs):

$ capslock -goos darwin -goarch amd64 -output json -packages github.com/insomniacslk/dhcp/dhcpv4,github.com/miekg/dns 2>/dev/null | syscalls
syscall.Bind: {{Name:github.com/insomniacslk/dhcp/dhcpv4.MakeListeningSocket Site:{Filename:client.go Line:113 Column:23}}}
syscall.Sendto: {{Name:github.com/insomniacslk/dhcp/dhcpv4.BroadcastSendReceive Site:{Filename:client.go Line:213 Column:25}}}
syscall.SetsockoptInt: {{Name:github.com/insomniacslk/dhcp/dhcpv4.BindToInterface Site:{Filename:bindtodevice_darwin.go Line:15 Column:30}}, {Name:github.com/insomniacslk/dhcp/dhcpv4.MakeBroadcastSocket Site:{Filename:client.go Line:81 Column:29}}}
$ capslock -goos darwin -goarch amd64 -output json -packages github.com/miekg/dns,github.com/insomniacslk/dhcp/dhcpv4 2>/dev/null | syscalls
golang.org/x/sys/unix.CmsgLen: {{Name:golang.org/x/net/internal/socket.controlHeaderLen Site:{Filename:cmsghdr_unix.go Line:13 Column:21}}}
golang.org/x/sys/unix.CmsgSpace: {{Name:golang.org/x/net/internal/socket.controlMessageSpace Site:{Filename:cmsghdr_unix.go Line:21 Column:23}}}
golang.org/x/sys/unix.SetsockoptInt: {{Name:github.com/miekg/dns.reuseportControl$1 Site:{Filename:listen_reuseport.go Line:19 Column:29}}}
syscall.Bind: {{Name:github.com/insomniacslk/dhcp/dhcpv4.MakeListeningSocket Site:{Filename:ipsock.go Line:180 Column:2}}}
syscall.Sendto: {{Name:github.com/insomniacslk/dhcp/dhcpv4.BroadcastSendReceive Site:{Filename:ipsock.go Line:278 Column:35}}}
syscall.SetsockoptInt: {{Name:github.com/insomniacslk/dhcp/dhcpv4.BindToInterface Site:{Filename:ipsock.go Line:296 Column:42}}, {Name:github.com/insomniacslk/dhcp/dhcpv4.MakeBroadcastSocket Site:{Filename:ipsock.go Line:151 Column:19}}}

Single package, multiple runs:

$ capslock -goos darwin -goarch amd64 -output json -packages github.com/miekg/dns 2>/dev/null | syscalls
golang.org/x/sys/unix.CmsgLen: {{Name:golang.org/x/net/internal/socket.controlHeaderLen Site:{Filename:cmsghdr_unix.go Line:13 Column:21}}}
golang.org/x/sys/unix.CmsgSpace: {{Name:golang.org/x/net/internal/socket.controlMessageSpace Site:{Filename:cmsghdr_unix.go Line:21 Column:23}}}
golang.org/x/sys/unix.SetsockoptInt: {{Name:github.com/miekg/dns.reuseportControl$1 Site:{Filename:listen_reuseport.go Line:19 Column:29}}}
$ capslock -goos darwin -goarch amd64 -output json -packages github.com/miekg/dns 2>/dev/null | syscalls
golang.org/x/sys/unix.CmsgLen: {{Name:golang.org/x/net/internal/socket.controlHeaderLen Site:{Filename:cmsghdr_unix.go Line:13 Column:21}}}
golang.org/x/sys/unix.CmsgSpace: {{Name:golang.org/x/net/internal/socket.controlMessageSpace Site:{Filename:cmsghdr_unix.go Line:21 Column:23}}}
golang.org/x/sys/unix.SetsockoptInt: {{Name:github.com/miekg/dns.reuseportControl$1 Site:{Filename:listen_reuseport.go Line:19 Column:29}}}

accept classifier interface in analyzer

Currently the public functions in package github.com/google/capslock/analyzer accept a *interesting.Classifier as an argument. For external usage of this package, it would be beneficial, if these functions would instead accept an interface as follows:

type Classifier interface {
	FunctionCategory(pkg string, name string) cpb.Capability
	IncludeCall(caller string, callee string) bool
}

This would allow users of this package to write their own classifiers or to extend the provided classifiers e.g. by wrapping them (middleware approach).

I am happy to provide a PR for this.

install documentation and packaging

hello o/

the install command from the documentation does not run on Ubuntu 23.04:

$ go install github.com/google/capslock/cmd/capslock@latest
go: github.com/google/capslock/cmd/capslock@latest: module github.com/google/capslock@latest found (v0.1.0), but does not contain package github.com/google/capslock/cmd/capslock

adding src/ does not work either:

$ go install github.com/google/capslock/src/cmd/capslock@latest
go: github.com/google/capslock/src/cmd/capslock@latest: github.com/google/capslock/[email protected]: parsing go.mod:
	module declares its path as: capslock
	       but was required as: github.com/google/capslock/src

however, entering ./src/cmd/capslock/ and running go install does work

capslock panics with Go1.22

:~/go/src/github.com/zalando/skipper]% make capslock
capslock -output=v -packages=./...
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x5cb67e]

goroutine 642 gp=0xc0012041c0 m=3 mp=0xc0000a0008 [running]:
panic({0x7e6620?, 0xb6d770?})
        /usr/share/go/src/runtime/panic.go:779 +0x158 fp=0xc001074ae8 sp=0xc001074a38 pc=0x43abb8
go/types.(*Checker).handleBailout(0xc001064200, 0xc001079bd0)
        /usr/share/go/src/go/types/check.go:367 +0x88 fp=0xc001074b08 sp=0xc001074ae8 pc=0x588308
go/types.(*Checker).checkFiles.deferwrap1()
        /usr/share/go/src/go/types/check.go:392 +0x25 fp=0xc001074b28 sp=0xc001074b08 pc=0x5888c5
panic({0x7e6620?, 0xb6d770?})
        /usr/share/go/src/runtime/panic.go:770 +0x132 fp=0xc001074bd8 sp=0xc001074b28 pc=0x43ab92
runtime.panicmem(...)
        /usr/share/go/src/runtime/panic.go:261
runtime.sigpanic()
        /usr/share/go/src/runtime/signal_unix.go:881 +0x378 fp=0xc001074c38 sp=0xc001074bd8 pc=0x453398
go/types.(*StdSizes).Sizeof(0x0, {0x8ed2e8, 0xb72da0})
        /usr/share/go/src/go/types/sizes.go:228 +0x31e fp=0xc001074c98 sp=0xc001074c38 pc=0x5cb67e
go/types.Sizes.Sizeof-fm({0x8ed2e8?, 0xb72da0?})
        <autogenerated>:1 +0x38 fp=0xc001074cc0 sp=0xc001074c98 pc=0x5ee178
go/types.(*Config).sizeof(...)
        /usr/share/go/src/go/types/sizes.go:333
go/types.representableConst.func1({0x8ed2e8?, 0xb72da0?})
        /usr/share/go/src/go/types/const.go:76 +0x9e fp=0xc001074d10 sp=0xc001074cc0 pc=0x58af9e
go/types.representableConst({0x8ef678, 0xb645b0}, 0xc001064200, 0xb72da0, 0xc001074e08)
        /usr/share/go/src/go/types/const.go:106 +0x2c7 fp=0xc001074de0 sp=0xc001074d10 pc=0x58a747
go/types.(*Checker).representation(0xc001064200, 0xc00106e580, 0xb72da0)
        /usr/share/go/src/go/types/const.go:256 +0x65 fp=0xc001074e28 sp=0xc001074de0 pc=0x58b325
go/types.(*Checker).representable(0xc001064200, 0xc00106e580, 0xb72da0)
        /usr/share/go/src/go/types/const.go:239 +0x26 fp=0xc001074e70 sp=0xc001074e28 pc=0x58b206
go/types.(*Checker).shift(0xc001064200, 0xc00106e4c0, 0xc00106e580, {0x8ee868, 0xc0010203f0}, 0x14)
        /usr/share/go/src/go/types/expr.go:650 +0x1eb fp=0xc001075110 sp=0xc001074e70 pc=0x59b2eb
go/types.(*Checker).binary(0xc001064200, 0xc00106e4c0, {0x8ee868, 0xc0010203f0}, {0x8ee6e8, 0xc0010265e0}, {0x8ee628, 0xc001026600}, 0x14, 0x7323)

Latest capslock was installed by:

go install github.com/google/capslock/cmd/capslock@latest

feature request: Capability diff between two versions of a given Go module/package.

First off, really happy to see the birth of the capslock tool as having a way to track capabilities used by transitive dependencies is key to mitigating supply-chain attacks.

One feature that would be really incredible to incorporate in capslock and associated tools is to easily diff the capabilities added/removed in between two versions of a given Go module/package.

And furthermore, make it possible to hook this functionality up to go get -u.

Imagine being able to run go get -u ./... to update packages of a Go module and having warnings be emitted for newly added capabilities of transitive dependencies.

E.g.

$ go get -u github.com/org/repo/pkg
WARNING: new capability added to `github.com/org/repo/pkg` (os/exec). Added in version 2023-09-26-githash.

Of course, neither go get nor capslock need to perform the capability diff itself, it could be a third glue tool that reads the versions (of updated dependencies) from go get -u and the json output of capabilities of capslock and prints warnings for newly added capabilities.

Once more, thanks for working to improve this space and help regain confidence in the capabilities utilized by dependencies in the open source community.

With cheerful regards,
Robin

incorrect CAPABILITY_NETWORK classification

When running capslock against one of my projects, I noticed some of the CAPABILITY_NETWORK classifications didn't seem to make sense. Digging into it further revealed that they were incorrect.

Running capslock at 29c2da0 against https://github.com/capnspacehook/egress-eddie/tree/faa23e15384d4a7f148e3bcb9fa30f3ab4d37d4c with capslock -packages github.com/capnspacehook/egress-eddie -output j displayed a few classifications like this:

{
  "packageName": "egresseddie",
  "capability": "CAPABILITY_NETWORK",
  "depPath": "github.com/capnspacehook/egress-eddie.parseConfigBytes github.com/BurntSushi/toml.Decode (*github.com/BurntSushi/toml.Decoder).Decode (*github.com/BurntSushi/toml.MetaData).unify (*github.com/BurntSushi/toml.MetaData).unifyText (net.pipeAddr).String",
  "path": [
    {
      "name": "github.com/capnspacehook/egress-eddie.parseConfigBytes"
    },
    {
      "name": "github.com/BurntSushi/toml.Decode",
      "site": {
        "filename": "config.go",
        "line": "90",
        "column": "24"
      }
    },
    {
      "name": "(*github.com/BurntSushi/toml.Decoder).Decode",
      "site": {
        "filename": "decode.go",
        "line": "36",
        "column": "51"
      }
    },
    {
      "name": "(*github.com/BurntSushi/toml.MetaData).unify",
      "site": {
        "filename": "decode.go",
        "line": "169",
        "column": "21"
      }
    },
    {
      "name": "(*github.com/BurntSushi/toml.MetaData).unifyText",
      "site": {
        "filename": "decode.go",
        "line": "213",
        "column": "22"
      }
    },
    {
      "name": "(net.pipeAddr).String",
      "site": {
        "filename": "decode.go",
        "line": "513",
        "column": "19"
      }
    }
  ],
  "packageDir": "github.com/capnspacehook/egress-eddie",
  "capabilityType": "CAPABILITY_TYPE_TRANSITIVE"
}

capslock seems to think toml.Decode is calling (net.pipeAddr).String eventually, but digging into the source reveals this is unlikely. (*github.com/BurntSushi/toml.MetaData).unifyText uses a type switch to create a string from an argument of type any. In the fmt.Stringer case capslock thinks that the now known fmt.Stringer type is the type net.pipeAddr. Source of the final call in the stack: https://github.com/BurntSushi/toml/blob/v1.2.1/decode.go#L513.

I understand that fmt.Stringer is an interface and apparently net.pipeAddr satisfies it, but it seems like capslock is assuming the concrete type of the fmt.Stringer here.

EDIT: after looking into this a bit more it seems this is just what golang.org/x/tools/go/ssa and golang.org/x/tools/go/callgraph reports and I'm not sure how difficult detecting this situation would be.

I tried to create a minimal reproducer the just called toml.Decode and some net functions so they would be loaded, but couldn't reproduce the same behavior unfortunately.

Thanks for building and open sourcing this tool, I've wanted something like this for a long time!

-output=compare exit code is 0 when differences are found

I saved the results of running capslock and removed some items to have compare find differences. However when I ran -output=compare capslock did find differences, but exited successfully. This makes it harder for CI systems to notice a change occurred and fail the build (or to notify humans/authors).

$ capslock -noisy -output=compare caps.json 
2023/11/30 16:21:53 Loaded package "ach"
Package github.com/moov-io/ach has new capability CAPABILITY_UNSAFE_POINTER compared to the baseline.
Package github.com/moov-io/ach has new capability CAPABILITY_FILES compared to the baseline.
Package github.com/moov-io/ach has new capability CAPABILITY_ARBITRARY_EXECUTION compared to the baseline.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.