layout | title | permalink | parent | grand_parent | nav_order |
---|---|---|---|---|---|
page |
Expanding your view of the software supply chain |
/expanding-your-view/ |
GUAC demos |
Getting started with GUAC |
1 |
Bruce Wayne : You're just SBOMs.
Henri Ducard (Ra's al Ghul) : No, no, no. An SBOM is just a document lost in the scramble for its own importance. It can be misplaced or underutilized. But if you make the SBOM more than just a document, you devote the SBOM to be utilized properly to secure your software supply chain, and if the attackers cannot get to you via supply chain attacks, then the SBOM becomes something else entirely.
Bruce Wayne : Which is?
Henri Ducard (Ra's al Ghul) : Legend, Mr. Wayne!
Ra’s al Ghul understands that an SBOM alone can be forgotten but if you combine it with GUAC, you can start to get a greater understanding of your own software supply chain environment.
In this demo, we will go through the process of ingesting an SBOM and letting GUAC expand our horizons on what we know about our environment autonomously!
We will demonstrate the various integrations of GUAC and how they work together within the ingested SBOM.
- A fresh copy of the [GUAC service infrastructure through Docker Compose]({{
site.baseurl }}{%link setup.md %}). Including the
guacone
binary in your path and GUAC Data extracted toguac-data-main
.
For demo purposes, let's ingest Vault’s SBOM. To do this, we will use the help
of the guacone collect files
command.
-
Run the following command:
guacone collect files guac-data-main/top-dh-sboms/vault.json
-
The output of the file collector should be:
{"level":"info","ts":1684774157.098919,"caller":"cmd/files.go:167","msg":"[2.054952875s] completed doc {Collector:FileCollector Source:file:///../guac-data/top-dh-sboms/vault.json}"} {"level":"info","ts":1684774157.098937,"caller":"cmd/files.go:174","msg":"collector ended gracefully"} {"level":"info","ts":1684774157.09894,"caller":"cmd/files.go:187","msg":"completed ingesting 1 documents of 1"}
As the ingestion process occurs, the collector subscriber service of GUAC collects pURLs, OCI strings, and others to determine if there is more information available to be pulled into the graph DB.
As the SBOM is ingested it:
- Collects the pURLs of its dependency packages
- Queries the deps.dev database automatically to grab the source, OpenSSF Scorecard, and its dependency information
- Links this information back to the original top-level artifact of the SBOM
This process is recursive, meaning that the pURLs that the dependency relies on will also be queried!
If you want to watch the collector subscriber work, see the checking the logs section at the bottom of this page.
As we saw above, GUAC automatically looks for more information for an ingested SBOM. What about vulnerabilities?
The certifier (currently utilizing the OSV database, with more integrations to come) is configured to run and query the vulnerability database to determine if a package has a vulnerability.
We will further inspect these vulnerabilities in the following section.
To understand what was collected, we will utilize the GraphQL playground. The
playground is accessible via: http://localhost:8080/graphql
From GraphQL Playground, we can use the provided GraphQL queries. Copy and paste the entire file into the left column of the playground. The below queries can be selected by name by using the "play" button in the center.
First, we will run the IsDependency
query by clicking the red play button and
selecting the IsDependency
.
The query:
IsDependency(
isDependencySpec: {dependencyPackage: {namespace: "github.com/prometheus", name: "client_golang"}}
)
The query will search all the IsDependency
nodes and find the one that depends
on the following package: pkg:golang/github.com/prometheus/client_golang
This will output the following:
{
"data": {
"IsDependency": [
{
"id": "399",
"justification": "top-level package GUAC heuristic connecting to each file/package",
"package": {
"id": "2",
"type": "guac",
"namespaces": [
{
"id": "3",
"namespace": "spdx/docker.io/library",
"names": [
{
"id": "4",
"name": "vault-latest",
"versions": [
{
"id": "5",
"version": "",
"qualifiers": [],
"subpath": ""
}
]
}
]
}
]
},
"dependencyPackage": {
"id": "6",
"type": "golang",
"namespaces": [
{
"id": "396",
"namespace": "github.com/prometheus",
"names": [
{
"id": "397",
"name": "client_golang",
"versions": []
}
]
}
]
},
"versionRange": "v1.11.1",
"origin": "file:///../guac-data/top-dh-sboms/vault.json",
"collector": "FileCollector"
},
{
"id": "7624",
"justification": "dependency data collected via deps.dev",
"package": {
"id": "6",
"type": "golang",
"namespaces": [
{
"id": "279",
"namespace": "github.com/armon",
"names": [
{
"id": "280",
"name": "go-metrics",
"versions": [
{
"id": "281",
"version": "v0.3.10",
"qualifiers": [],
"subpath": ""
}
]
}
]
}
]
},
"dependencyPackage": {
"id": "6",
"type": "golang",
"namespaces": [
{
"id": "396",
"namespace": "github.com/prometheus",
"names": [
{
"id": "397",
"name": "client_golang",
"versions": []
}
]
}
]
},
"versionRange": "v1.4.0",
"origin": "deps.dev",
"collector": "deps.dev"
},
{
"id": "8508",
"justification": "dependency data collected via deps.dev",
"package": {
"id": "6",
"type": "golang",
"namespaces": [
{
"id": "503",
"namespace": "github.com/docker",
"names": [
{
"id": "504",
"name": "docker",
"versions": [
{
"id": "505",
"version": "v20.10.10+incompatible",
"qualifiers": [],
"subpath": ""
}
]
}
]
}
]
},
"dependencyPackage": {
"id": "6",
"type": "golang",
"namespaces": [
{
"id": "396",
"namespace": "github.com/prometheus",
"names": [
{
"id": "397",
"name": "client_golang",
"versions": []
}
]
}
]
},
"versionRange": "v1.15.0",
"origin": "deps.dev",
"collector": "deps.dev"
}
]
}
}
From the output, we can see that prometheus/client_golang
is used by a bunch
of packages. The first one shows the origin as the document that we ingested at
the beginning (related to vault). The other entries all come from deps.dev that
show that other packages github.com/armon/go-metrics
also depend on
prometheus/client_golang
. Meaning that prometheus/client_golang
is both a
direct and transitive dependency for the Vault image SBOM we ingested!
Next we will run the HasSourceAt
query by clicking the red play button and
selecting the HasSourceAt
.
The query:
HasSourceAt(
hasSourceAtSpec: {package: {namespace: "cloud.google.com", name: "go"}}
)
The query will search all the HasSourceAt
nodes and find the one related to
the package specified above.
This will output the following:
"HasSourceAt": [
{
"id": "7046",
"justification": "collected via deps.dev",
"knownSince": "2023-04-20T12:39:26.823782Z",
"package": {
"id": "6",
"type": "golang",
"namespaces": [
{
"id": "1075",
"namespace": "cloud.google.com",
"names": [
{
"id": "1076",
"name": "go",
"versions": []
}
]
}
]
},
"source": {
"id": "6964",
"type": "git",
"namespaces": [
{
"id": "6965",
"namespace": "github.com/googleapis",
"names": [
{
"id": "6966",
"name": "google-cloud-go",
"tag": "",
"commit": ""
}
]
}
]
},
"origin": "deps.dev",
"collector": "deps.dev"
}
]
The collector subscriber and deps.dev collector captured that the
pkg:golang/cloud.google.com/go
has a source repo at
github.com/googleapis/google-cloud-go
. This information shows the origin being
deps.dev.
Next, we will run the Scorecard
query by clicking the red play button and
selecting the Scorecard
.
The query:
scorecards(
scorecardSpec: {source: {namespace: "github.com/googleapis", name: "google-cloud-go"}}
)
The query will search all the scorecard
nodes and find the one related to the
source specified above.
This will output the following:
"scorecards": [
{
"id": "6967",
"source": {
"id": "6964",
"type": "git",
"namespaces": [
{
"id": "6965",
"namespace": "github.com/googleapis",
"names": [
{
"id": "6966",
"name": "google-cloud-go",
"tag": "",
"commit": ""
}
]
}
]
},
"scorecard": {
"timeScanned": "2023-04-10T00:00:00Z",
"aggregateScore": 8.300000190734863,
"checks": [
{
"check": "License",
"score": 10
},
{
"check": "Signed-Releases",
"score": -1
},
{
"check": "Dangerous-Workflow",
"score": 10
},
{
"check": "Token-Permissions",
"score": 0
},
{
"check": "Maintained",
"score": 10
},
{
"check": "Branch-Protection",
"score": -1
},
{
"check": "Packaging",
"score": -1
},
{
"check": "Security-Policy",
"score": 10
},
{
"check": "Fuzzing",
"score": 10
},
{
"check": "Binary-Artifacts",
"score": 10
},
{
"check": "Pinned-Dependencies",
"score": 7
},
{
"check": "Vulnerabilities",
"score": 10
},
{
"check": "CII-Best-Practices",
"score": 0
}
],
"scorecardVersion": "v4.10.5-30-gfade79b",
"scorecardCommit": "fade79ba6b60232f6ac38070f9f4a388f7580d90",
"origin": "deps.dev",
"collector": "deps.dev"
}
}
]
The above source repo we found at github.com/googleapis/google-cloud-go
now
has the following scorecard attached to it with a timestamp on when the OpenSSF
Scorecard was taken. Once again we see that we collected this information
automatically from deps.dev!
Finally we will run the CertifyVuln
query by clicking the red play button and
selecting the CertifyVuln
.
The query:
CertifyVuln(
certifyVulnSpec: {vulnerability: {osv: {osvId: "ghsa-cg3q-j54f-5p7p"}}}
)
The query will search all the CertifyVuln
nodes and find the one that relates
to the OSV ID specified above.
This will output the following:
{
"data": {
"CertifyVuln": [
{
"id": "9699",
"package": {
"id": "6",
"type": "golang",
"namespaces": [
{
"id": "191",
"namespace": "github.com/prometheus",
"names": [
{
"id": "192",
"name": "client_golang",
"versions": [
{
"id": "7623",
"version": "v1.4.0",
"qualifiers": [],
"subpath": ""
}
]
}
]
}
]
},
"vulnerability": {
"__typename": "OSV",
"id": "9698",
"osvId": "ghsa-cg3q-j54f-5p7p"
},
"metadata": {
"dbUri": "",
"dbVersion": "",
"scannerUri": "osv.dev",
"scannerVersion": "0.0.14",
"timeScanned": "2023-04-20T12:41:38.272364Z",
"origin": "guac",
"collector": "guac"
}
},
{
"id": "14527",
"package": {
"id": "6",
"type": "golang",
"namespaces": [
{
"id": "191",
"namespace": "github.com/prometheus",
"names": [
{
"id": "192",
"name": "client_golang",
"versions": [
{
"id": "14106",
"version": "v1.7.1",
"qualifiers": [],
"subpath": ""
}
]
}
]
}
]
},
"vulnerability": {
"__typename": "OSV",
"id": "9698",
"osvId": "ghsa-cg3q-j54f-5p7p"
},
"metadata": {
"dbUri": "",
"dbVersion": "",
"scannerUri": "osv.dev",
"scannerVersion": "0.0.14",
"timeScanned": "2023-04-20T12:46:46.985135Z",
"origin": "guac",
"collector": "guac"
}
}
]
}
}
This information came from the OSV certifier service that is constantly running
within GUAC. From this, we can see that two versions of
github.com/prometheus/client_golang
contain the same ghsa-cg3q-j54f-5p7p
. In
the [vulnerability CLI demo]({{ site.baseurl }}{%link
querying-via-cli.md %}), we can use this information to determine if there is a
path between this and the version of Vault we are using. Here is a quick look at
what the visualization would look like for that:
Through this demo, we learned that GUAC services are designed to extract as much information as possible about an SBOM that it ingests. Utilizing this information, we can quickly make up-to-date policy decisions. We can even integrate GUAC services into an IDE to provide information on whether or not a package should be used due to a low OpenSSF Scorecard score or critical vulnerability.
Next, let's see [what is known and unknown about your software supply chain]({{ site.baseurl}}{%link known-and-unknown.md %}).
We can pull the logs from docker to see the progress of the ingestion:
docker logs guac-guac-ingestor-1
The results for the Vault SBOM ingestion will look like the following:
{"level":"info","ts":1681992933.3817039,"caller":"emitter/nats_emitter.go:121","msg":"creating stream \"DOCUMENTS\" and subjects \"DOCUMENTS.*\""}
{"level":"info","ts":1681994359.306624,"caller":"process/process.go:97","msg":"[processor: 856ef1f5-1627-4ec9-b9e1-507b7bdee58f] docTree Processed: {Collector:FileCollector Source:file:///../guac-data/top-dh-sboms/vault.json}"}
{"level":"info","ts":1681994359.31475,"caller":"parser/parser.go:128","msg":"parsing document tree with root type: SPDX"}
{"level":"info","ts":1681994359.3263202,"caller":"helpers/assembler.go:34","msg":"assembling CertifyScorecard: 0"}
{"level":"info","ts":1681994359.32635,"caller":"helpers/assembler.go:39","msg":"assembling IsDependency: 2260"}
{"level":"info","ts":1681994359.7843368,"caller":"helpers/assembler.go:44","msg":"assembling IsOccurence: 963"}
{"level":"info","ts":1681994359.954613,"caller":"helpers/assembler.go:49","msg":"assembling HasSLSA: 0"}
{"level":"info","ts":1681994359.954643,"caller":"helpers/assembler.go:54","msg":"assembling CertifyVuln: 0"}
{"level":"info","ts":1681994359.954647,"caller":"helpers/assembler.go:59","msg":"assembling IsVuln: 0"}
{"level":"info","ts":1681994359.954649,"caller":"helpers/assembler.go:64","msg":"assembling HasSourceAt: 0"}
{"level":"info","ts":1681994359.9546518,"caller":"helpers/assembler.go:69","msg":"assembling CertifyBad: 0"}
{"level":"info","ts":1681994359.954654,"caller":"helpers/assembler.go:74","msg":"assembling CertifyGood: 0"}
{"level":"info","ts":1681994359.9546711,"caller":"cmd/ingest.go:118","msg":"got collect entries to add: 349"}
{"level":"info","ts":1681994359.9560268,"caller":"parser/parser.go:110","msg":"[ingestor: 04462d2a-a2c7-4aa9-95eb-2183cb5f249d] ingested docTree: {Collector:FileCollector Source:file:///../guac-data/top-dh-sboms/vault.json}"}
We can pull the logs from docker to see which packages deps.dev collector found:
docker logs guac-depsdev-collector-1
The results from the deps.dev collector logs will look like the following:
{"level":"info","ts":1681994369.748968,"caller":"deps_dev/deps_dev.go:217","msg":"obtained additional metadata for package: pkg:golang/cloud.google.com/[email protected]"}
{"level":"info","ts":1681994372.493675,"caller":"deps_dev/deps_dev.go:217","msg":"obtained additional metadata for package: pkg:golang/cloud.google.com/go/[email protected]"}
{"level":"info","ts":1681994375.3482509,"caller":"deps_dev/deps_dev.go:217","msg":"obtained additional metadata for package: pkg:golang/cloud.google.com/go/[email protected]"}
{"level":"info","ts":1681994376.722956,"caller":"deps_dev/deps_dev.go:217","msg":"obtained additional metadata for package: pkg:golang/code.cloudfoundry.org/[email protected]"}
{"level":"info","ts":1681994377.476279,"caller":"deps_dev/deps_dev.go:217","msg":"obtained additional metadata for package: pkg:golang/github.com/Azure/[email protected]"}
{"level":"info","ts":1681994380.7538428,"caller":"deps_dev/deps_dev.go:217","msg":"obtained additional metadata for package: pkg:golang/github.com/Azure/[email protected]+incompatible"}
{"level":"info","ts":1681994382.8232992,"caller":"deps_dev/deps_dev.go:217","msg":"obtained additional metadata for package: pkg:golang/github.com/Azure/[email protected]"}
If we go back to the ingestor logs, we will see deps.dev documents being ingested.
docker logs guac-guac-ingestor-1
These logs will show the following with the collector and source being from deps.dev.
{"level":"info","ts":1681994398.146413,"caller":"parser/parser.go:128","msg":"parsing document tree with root type: DEPS_DEV"}
{"level":"info","ts":1681994398.146731,"caller":"helpers/assembler.go:34","msg":"assembling CertifyScorecard: 1"}
{"level":"info","ts":1681994398.148156,"caller":"helpers/assembler.go:39","msg":"assembling IsDependency: 12"}
{"level":"info","ts":1681994398.156023,"caller":"helpers/assembler.go:44","msg":"assembling IsOccurence: 0"}
{"level":"info","ts":1681994398.156051,"caller":"helpers/assembler.go:49","msg":"assembling HasSLSA: 0"}
{"level":"info","ts":1681994398.1560571,"caller":"helpers/assembler.go:54","msg":"assembling CertifyVuln: 0"}
{"level":"info","ts":1681994398.156063,"caller":"helpers/assembler.go:59","msg":"assembling IsVuln: 0"}
{"level":"info","ts":1681994398.156069,"caller":"helpers/assembler.go:64","msg":"assembling HasSourceAt: 4"}
{"level":"info","ts":1681994398.157971,"caller":"helpers/assembler.go:69","msg":"assembling CertifyBad: 0"}
{"level":"info","ts":1681994398.1579862,"caller":"helpers/assembler.go:74","msg":"assembling CertifyGood: 0"}
{"level":"info","ts":1681994398.157998,"caller":"cmd/ingest.go:118","msg":"got collect entries to add: 12"}
{"level":"info","ts":1681994398.158665,"caller":"parser/parser.go:110","msg":"[ingestor: 04462d2a-a2c7-4aa9-95eb-2183cb5f249d] ingested docTree: {Collector:deps.dev Source:deps.dev}"}
From the logs we see that CertifyScorecard
, IsDependency
and HasSourceAt
are being ingested.
We can pull the logs from docker to see the OSV certifier in action.
docker logs guac-osv-certifier-1
The results from the osv certifier logs will look like the following:
{"level":"info","ts":1681994498.498469,"caller":"cmd/osv.go:115","msg":"[209.458µs] completed doc {Collector:guac Source:guac}"}
{"level":"info","ts":1681994498.4986901,"caller":"cmd/osv.go:115","msg":"[216µs] completed doc {Collector:guac Source:guac}"}
{"level":"info","ts":1681994498.4989061,"caller":"cmd/osv.go:115","msg":"[211.042µs] completed doc {Collector:guac Source:guac}"}
{"level":"info","ts":1681994498.498911,"caller":"cmd/osv.go:122","msg":"certifier ended gracefully"}