Skip to content

Commit

Permalink
Add schemas and docs for SBOM, inventory, and configuration plugins (#…
Browse files Browse the repository at this point in the history
…397)

* Document inventory and configuration findings.

* Restrict type to values from API spec.

* Handle non-array details.

In the case of "inventory" plugins, the details is can be anything (i.e.
not necessarily an array).

* Add SBOM finding schema.

* Add unit test for SBOM.
  • Loading branch information
ZoogieZork authored Dec 20, 2024
1 parent cd6618a commit 818cdaa
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 83 deletions.
67 changes: 64 additions & 3 deletions backend/engine/plugins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,76 @@ Example:

### SBOM

TODO: Add documentation here
SBOM ([software bill of materials](https://en.wikipedia.org/wiki/Software_supply_chain)) plugins gather an inventory of software components such as library dependencies.

The `details` returned is a 2-element array.

The first element is an array of SBOMs. These are not modified and are saved as-is for later retrieval. The specific format depends on the plugin, but should be a standard JSON format such as [CycloneDX](https://cyclonedx.org/) or [SPDX](https://spdx.dev/). This may be an empty array if no user-downloadable SBOMs are generated.

The second element is an array of detected components, with the following fields:

- `bom-ref`: Unique reference ID for this component.
- `type`: Component type (e.g. `jar`, `gomod`, etc.). This is tool-specific. For example, see the [list of types for Trivy](https://github.com/aquasecurity/trivy/blob/49f354085fdaf0f45f8f8f52c9a2a06fffbc2e63/pkg/fanal/analyzer/const.go).
- `name`: Component name, such as a package ID or filename.
- `version`: Component version. If not available or does not apply for this component type, must be `none`.
- `licenses`: Array of licenses:
- `id`: The [SPDX license identifier](https://spdx.org/licenses/).
- `name`: The license name.

Full example:

```jsonc
[
[
{ /* SBOM for component 1... */ },
{ /* SBOM for component 2... */ }
],
[
{
"bom-ref": "pkg:golang/cloud.google.com/go/[email protected]",
"type": "gomod",
"name": "cloud.google.com/go/datastore",
"version": "1.1.0",
"licenses": [
{
"id": "Apache-2.0",
"name": "Apache-2.0"
}
]
}
]
]
```

### Inventory

TODO: Add documentation here
Inventory plugins generate statistics such as technologies used or declared dependencies, either for purely informational or audit purposes.

These typically do not make judgements about the security impact of the data collected.

The details payload for inventory plugins is specific to each plugin.

### Configuration

TODO: Add documentation here
Configuration plugins evaluate the security settings of a repository. The `details` returned is a list of objects. Each distinct issue should have its own entry in the list.

The fields are:

- `name`: The name of the issue.
- `description`: Long-form description of the issue.
- `severity`: The severity level of the issue: critical, high, medium, low, or negligible.

Example:

```json
[
{
"name": "Branch Rule - Require Status Checks",
"description": "Requires that a branch rule is enabled that requires status checks on pull requests",
"severity": "medium"
}
]
```

## Local Testing

Expand Down
3 changes: 3 additions & 0 deletions backend/utilities/plugin_runner/toolbox/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ func mustLoadSchema(id string) any {

// Plugin type -> schema ID.
var pluginTypeSchemaMap = map[string]string{
"configuration": "configuration-finding",
"inventory": "unknown-finding", // Open-ended schema.
"secrets": "secrets-finding",
"sbom": "sbom-finding",
"static_analysis": "static-analysis-finding",
"vulnerability": "vulnerability-finding",
}
Expand Down
84 changes: 84 additions & 0 deletions backend/utilities/plugin_runner/toolbox/lint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,90 @@ func TestValidSecrets(t *testing.T) {
}
}

func TestValidConfiguration(t *testing.T) {
actual := lint("configuration", []byte(`{
"success": true,
"truncated": false,
"details": [{
"name": "Branch Rule - Require Status Checks",
"description": "Requires that a branch rule is enabled that requires status checks on pull requests",
"severity": "medium"
}],
"errors": ["failed to scan"]
}`))
if actual != nil {
t.Fatalf("expected no errors, got %v", actual)
}
}

func TestValidInventory(t *testing.T) {
// The "details" payload for inventory plugins can be anything.

actual := lint("inventory", []byte(`{
"success": true,
"truncated": false,
"details": {"foo": {"bar": "baz"}},
"errors": ["failed to scan"]
}`))
if actual != nil {
t.Fatalf("expected no errors, got %v", actual)
}

actual = lint("inventory", []byte(`{
"success": true,
"truncated": false,
"details": [["foo", false]],
"errors": ["failed to scan"]
}`))
if actual != nil {
t.Fatalf("expected no errors, got %v", actual)
}
}

func TestValidSBOM(t *testing.T) {
actual := lint("sbom", []byte(`{
"success": true,
"truncated": false,
"details": [
[
{"sbom": "foo"}
],
[
{
"bom-ref": "pkg:golang/cloud.google.com/go/[email protected]",
"type": "gomod",
"name": "cloud.google.com/go/datastore",
"version": "1.1.0",
"licenses": [
{
"id": "Apache-2.0",
"name": "Apache-2.0"
}
]
}
]
],
"errors": ["failed to scan"]
}`))
if actual != nil {
t.Fatalf("expected no errors, got %v", actual)
}
}

func TestSBOMMissingValue(t *testing.T) {
actual := lint("sbom", []byte(`{
"success": true,
"truncated": false,
"details": [
[]
],
"errors": ["failed to scan"]
}`))
if !containsValidationError(actual, "/details", "minItems: got 1, want 2") {
t.Fatalf("expected required error, got %v", actual)
}
}

func TestUnknownType(t *testing.T) {
actual := lint("foo", []byte("{}"))
if actual == nil || !strings.Contains(actual.Error(), "unknown plugin type") {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$id": "https://wbd.com/artemis/plugin/configuration-finding.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",

"title": "ConfigurationFinding",
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"description",
"severity"
],
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"severity": {
"enum": ["critical", "high", "medium", "low", "negligible"]
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@
"truncated": {
"enum": [false]
},
"details": {
"type": "array",
"items": { "$ref": "finding.json" }
},
"details": { "$ref": "finding.json" },
"errors": {
"type": "array",
"items": { "type": "string" }
Expand Down
55 changes: 55 additions & 0 deletions backend/utilities/plugin_runner/toolbox/schemas/sbom-finding.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"$id": "https://wbd.com/artemis/plugin/sbom-finding.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",

"title": "SBOMFinding",
"type": "array",
"items": false,
"prefixItems": [
{
"type": "array",
"items": { "type": "object" }
},
{
"type": "array",
"items": { "$ref": "#/$defs/component" }
}
],
"minItems": 2,
"maxItems": 2,

"$defs": {
"component": {
"type": "object",
"required": [
"bom-ref",
"type",
"name",
"version",
"licenses"
],
"properties": {
"bom-ref": { "type": "string" },
"type": { "type": "string" },
"name": { "type": "string" },
"version": { "type": "string" },
"licenses": {
"type": "array",
"items": { "$ref": "#/$defs/license" }
}
}
},

"license": {
"type": "object",
"required": [
"id",
"name"
],
"properties": {
"id": { "type": "string" },
"name": { "type": "string" }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,42 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",

"title": "SecretsFinding",
"type": "object",
"required": [
"filename",
"line",
"commit",
"type",
"author"
],
"properties": {
"filename": {
"type": "string"
},
"line": {
"type": "integer"
},
"commit": {
"type": "string"
},
"type": {
"type": "string"
},
"author": {
"type": "string"
"type": "array",
"items": {
"type": "object",
"required": [
"filename",
"line",
"commit",
"type",
"author"
],
"properties": {
"filename": {
"type": "string"
},
"line": {
"type": "integer"
},
"commit": {
"type": "string"
},
"type": {
"enum": [
"aws",
"ssh",
"mongo",
"postgres",
"redis",
"urlauth",
"google",
"slack",
"other"
]
},
"author": {
"type": "string"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,32 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",

"title": "StaticAnalysisFinding",
"type": "object",
"required": [
"filename",
"line",
"message",
"severity",
"type"
],
"properties": {
"filename": {
"type": "string"
},
"line": {
"type": "integer"
},
"message": {
"type": "string"
},
"severity": {
"enum": ["critical", "high", "medium", "low", "negligible"]
},
"type": {
"type": "string"
"type": "array",
"items": {
"type": "object",
"required": [
"filename",
"line",
"message",
"severity",
"type"
],
"properties": {
"filename": {
"type": "string"
},
"line": {
"type": "integer"
},
"message": {
"type": "string"
},
"severity": {
"enum": ["critical", "high", "medium", "low", "negligible"]
},
"type": {
"type": "string"
}
}
}
}
Loading

0 comments on commit 818cdaa

Please sign in to comment.