Coder Social home page Coder Social logo

gocap's Introduction

A take on supply chain security in Go

List your dependencies capabilities and monitor if dependency updates require more capabilities.

The Problem

Recently different attacks and other issues related to open-source dependencies highlighted a quite severe problem with dependencies:

Every imported package gives that package's author basically remote code execution for your software.

The Idea

A video on WASI by linclark brought me to the idea how cool it would be if we could pass permissions to our dependencies.

In Go this could look something like this:

module github.com/cugu/gocap

go 1.20

require (
	github.com/go-chi/chi       v5.0.7   (network:read)
	github.com/mattn/go-sqlite3 v1.14.10 (file:read, file:write)
	github.com/sirupsen/logrus  v1.8.1   (os:stdout)
	github.com/yuin/goldmark    v1.4.4   ()
)

chi would be able to receive network requests, go-sqlite3 would be able to read and write files and logrus could write to stdout. But also all those modules would be limited to those capabilities and, for example, the logging library logrus would not be able to interact with files, the network or execute code.

Changes of dependencies would be much less critical in many cases, as a potential attacker would have only limited attack surface besides stealing your CPU cycles.

A simpler but working approach: GoCap

Implementing the approach above would require changes to Go itself. So I came up with another, simpler approach: GoCap. GoCap can check and validate the source code of dependencies for their capabilities and is ment to be included into the testing phase of the build process. This way GoCap can at least pin the capabilities of dependencies.

GoCap provides simple capability checking for Go using a go.cap file. The go.cap files lists all package dependencies that require critical permissions like file access, execution rights or network access. Unlike the idea above GoCap works on packages not modules and capabilities are based on the imported packages of the standard library.

The go.cap file for GoCap itself looks like this:

github.com/cugu/gocap (execute, file)

github.com/alecthomas/kong (file, syscall)
github.com/pkg/errors (runtime)

Install GoCap

You can download a release or run

go install github.com/cugu/[email protected]

gocap generate

gocap generate <path> prints a valid go.cap file. It lists all dependency packages that require critical permissions like file access, execution rights or network access.

! gocap generate runs on package basis and the path argument must point to a Go package (there must be .go files) not a Go Module. Also gocap generate needs the dependencies downloaded before, e.g. run go mod download.

Example

cd ~/myProjects/gocap
go mod download
gocap generate . > go.cap

go.cap

github.com/cugu/gocap (execute, file)

github.com/alecthomas/kong (file, syscall)
github.com/pkg/errors (runtime)

gocap check

gocap check <path> compares a local go.cap file with the actual required capabilities by dependency packages. Any mismatch results in a non-zero exit code, so you can use GoCap check in your CI pipelines. See ci.yml for a working example.

Example

gocap check .

Output

github.com/alecthomas/kong
	capability 'syscall' not provided by go.cap file, add to go.cap file if you want to grant the capability
github.com/pkg/errors
	unnecessary capability 'network', please remove from go.cap file

Capabilities

Name Description Packages
file Read and write access to files os io/ioutil
network Read and write to the network net net/http
execute Execute other binaries os/exec
syscall Perform any system call in context of the software syscall, C
unsafe Usage of the unsafe package in combination with a //go:linkname compiler directive can be used to load external C or assembler code unsafe
reflect reflect.NewAt in combination with reflect#Value.Call might be used call arbitrary functions reflect

gocap's People

Contributors

cugu avatar lemon-mint avatar renovate-bot avatar renovate[bot] avatar sideninja 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

gocap's Issues

ux: go check should generate .cap if does not exist

Thanks for the such a cool project! Just have been trying this out!


I think it had better to call generate command under the hood if go.cap file does not exit. Or we can rephrase this warning to: go.cap file does not exist, consider run generate command for example.

$ gocap check .
go.cap file does not exist

What do you think?

Still supported?

This looks like a handy tool, but it hasn't been touched in a while. Is it still supported?

I get the following errors when I run it:

$ gocap generate .
could not parse package path

$ gocap generate ./...
invalid character '{' after top-level value

I'm running from within a go.mod managed directory where dependencies are vendored.

Ignore check for my package from my project

For example for my local project:

mkdir -p /tmp/myWonderfullProject/cmd/myWonderfullProject

cd /tmp/myWonderfullProject

echo 'package main

import (
	"net/http"

	"myWonderfullProject"
)

func main() {
	http.ListenAndServe(":8080", myWonderfullProject.Router())
}' > /tmp/myWonderfullProject/cmd/myWonderfullProject/main.go

echo 'package myWonderfullProject

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func Router() http.Handler {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})

	return r
}' > /tmp/myWonderfullProject/router.go

go mod tidy
tree /tmp/myWonderfullProject/
/tmp/myWonderfullProject/
├── cmd
│   └── myWonderfullProject
│       └── main.go
├── go.mod
├── go.sum
└── router.go


gocap generate /tmp/myWonderfullProject/cmd/myWonderfullProject
myWonderfullProject/cmd/myWonderfullProject (network)

github.com/gin-contrib/sse (file, network)
github.com/gin-gonic/gin (file, network, runtime)
github.com/gin-gonic/gin/binding (network, file)
github.com/gin-gonic/gin/render (network)
github.com/go-playground/universal-translator (file)
github.com/go-playground/validator/v10 (file, network)
golang.org/x/sys/unix (syscall, runtime)
myWonderfullProject (network) <------ filter this package on generate & check

Is it possible to filter all the packages from myWonderfullProject or at least myWonderfullProject (network) in this example on generate and on check steps?

Because, I want to avoid breaking my CI when I develop or refactor source code and also avoid updating too frequently the go.cap (because just add local package)?
From my point of view, this tool seems great for watching the external dependencies when I upgrade them.

doesn't work with my local project

in my local project (that means directory with go.mod & go.sum), when I run

cd myWonderfullProject

ls 
go.mod  go.sum main.go

gocap generate .
could not parse package path

I expect that go.cap has been generated with all modules (direct & transitive) pined into my go.mod & go.sum.

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.