Skip to content

Commit

Permalink
code owners enricher (#47)
Browse files Browse the repository at this point in the history
* experimental cdxgen introduction

* add params and image

* add sbom printing capabilities to the stdou-json consumer

* producer, example pipeline

* add annotation caps to stdout-json consumer

* lint

* lint

* wip code owners enricher

* add the ability to enrich sbom document with security scorecard score

* lint:

* codeowners enricher done

* remove unused test build target
  • Loading branch information
northdpole authored Oct 18, 2023
1 parent bbe655d commit da3f67a
Show file tree
Hide file tree
Showing 9 changed files with 298 additions and 2 deletions.
36 changes: 36 additions & 0 deletions components/enrichers/codeowners/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
subinclude(
"//build/defs:dracon",
"//build/defs:buildkit",
)

go_binary(
name = "codeowners",
srcs = [
"main.go",
],
static = True,
deps = [
"//api/proto/v1",
"//pkg/putil",
"//third_party/go/github.com/hairyhenderson/go-codeowners",
"//third_party/go/github.com/package-url/packageurl-go",
"//third_party/go/google.golang.org/protobuf",
],
)

buildkit_distroless_image(
name = "image",
srcs = [":codeowners"],
visibility = [
"//examples/...",
],
)

dracon_component(
name = "codeowners",
images = [
":image",
],
task = "task.yaml",
visibility = ["//examples/pipelines/..."],
)
5 changes: 5 additions & 0 deletions components/enrichers/codeowners/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CodeOwners Enricher

This enricher scans the cloned source for [CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) files,
For each finding, it adds the following annotation.
"Owner-<incremental number>:<the username of the owner>"
93 changes: 93 additions & 0 deletions components/enrichers/codeowners/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# DO NOT EDIT. Code generated by:
# github.com/ocurity/dracon//build/tools/kustomize-component-generator.

apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
resources:
- task.yaml
patches:
# Add the Task to the Tekton Pipeline.
- patch: |
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: unused
spec:
workspaces:
- name: source-code-ws
tasks:
- name: enricher-codeowners
taskRef:
name: enricher-codeowners
workspaces:
- name: source-code-ws
workspace: source-code-ws
params:
- name: enricher-codeowners-annotation
value: $(params.enricher-codeowners-annotation)
params:
- name: enricher-codeowners-annotation
type: string
default: ""
target:
kind: Pipeline
# Add anchors to Task.
- patch: |
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: enricher-codeowners
labels:
v1.dracon.ocurity.com/component: enricher
spec:
params:
- name: anchors
type: array
description: A list of tasks that this task depends on using their anchors.
default: []
results:
- name: anchor
description: An anchor to allow other tasks to depend on this task.
steps:
- name: anchor
image: docker.io/busybox:1.35.0
script: echo "$(context.task.name)" > "$(results.anchor.path)"
target:
kind: Task
name: enricher-codeowners
# If we have an producer-aggregator task in the pipeline (added by the
# producer-aggregator component), make the enricher depend on the completion of
# it.
- patch: |
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: unused
spec:
tasks:
- name: enricher-codeowners
params:
- name: anchors
value:
- $(tasks.producer-aggregator.results.anchor)
target:
kind: Pipeline
annotationSelector: v1.dracon.ocurity.com/has-producer-aggregator=true
# If we have a enricher-aggregator task in the pipeline (added by the
# enricher-aggregator component), make it depend on the completion of this
# enricher.
- patch: |
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: unused
spec:
tasks:
- name: enricher-aggregator
params:
- name: anchors
value:
- $(tasks.enricher-codeowners.results.anchor)
target:
kind: Pipeline
annotationSelector: v1.dracon.ocurity.com/has-enricher-aggregator=true
125 changes: 125 additions & 0 deletions components/enrichers/codeowners/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Package main of the codeowners enricher
// handles enrichment of individual issues with
// the groups/usernames listed in the github repository
// CODEOWNERS files.
// Owners are matched against the "target" field of the issue
package main

import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"

owners "github.com/hairyhenderson/go-codeowners"
v1 "github.com/ocurity/dracon/api/proto/v1"
"github.com/ocurity/dracon/pkg/putil"
)

const defaultAnnotation = "Owner"

var (
readPath string
writePath string
repoBasePath string
annotation string
)

func lookupEnvOrString(key string, defaultVal string) string {
if val, ok := os.LookupEnv(key); ok {
return val
}
return defaultVal
}

func enrichIssue(i *v1.Issue) (*v1.EnrichedIssue, error) {
enrichedIssue := v1.EnrichedIssue{}
annotations := map[string]string{}
targets := []string{}
if i.GetCycloneDXSBOM() != "" {
// shortcut, if there is a CycloneDX BOM then there is no target.
// we get the url from the repoURL parameter
targets = []string{"."}
} else {
target := strings.Split(i.GetTarget(), ":")
if len(target) > 1 {
targets = append(targets, target[0])
} else {
targets = append(targets, i.GetTarget())
}
}
for _, target := range targets {
path := filepath.Join(repoBasePath, target)
c, err := owners.FromFile(repoBasePath)
if err != nil {
log.Println("could not instantiate owners for path", path, "err", err)
continue
}
owners := c.Owners(path)
for _, owner := range owners {
annotations[fmt.Sprintf("Owner-%d", len(annotations))] = owner
}
}

enrichedIssue = v1.EnrichedIssue{
RawIssue: i,
Annotations: annotations,
}
enrichedIssue.Annotations = annotations
return &enrichedIssue, nil
}

func run() {
res, err := putil.LoadTaggedToolResponse(readPath)
if err != nil {
log.Fatalf("could not load tool response from path %s , error:%v", readPath, err)
}
if annotation == "" {
annotation = defaultAnnotation
}
for _, r := range res {
enrichedIssues := []*v1.EnrichedIssue{}
for _, i := range r.GetIssues() {
eI, err := enrichIssue(i)
if err != nil {
log.Println(err)
continue
}
enrichedIssues = append(enrichedIssues, eI)
}
if len(enrichedIssues) > 0 {
if err := putil.WriteEnrichedResults(r, enrichedIssues,
filepath.Join(writePath, fmt.Sprintf("%s.depsdev.enriched.pb", r.GetToolName())),
); err != nil {
log.Fatal(err)
}
} else {
log.Println("no enriched issues were created for", r.GetToolName())
}
if len(r.GetIssues()) > 0 {
scanStartTime := r.GetScanInfo().GetScanStartTime().AsTime()
if err := putil.WriteResults(
r.GetToolName(),
r.GetIssues(),
filepath.Join(writePath, fmt.Sprintf("%s.raw.pb", r.GetToolName())),
r.GetScanInfo().GetScanUuid(),
scanStartTime.Format(time.RFC3339),
); err != nil {
log.Fatalf("could not write results: %s", err)
}
}

}
}

func main() {
flag.StringVar(&readPath, "read_path", lookupEnvOrString("READ_PATH", ""), "where to find producer results")
flag.StringVar(&writePath, "write_path", lookupEnvOrString("WRITE_PATH", ""), "where to put enriched results")
flag.StringVar(&annotation, "annotation", lookupEnvOrString("ANNOTATION", defaultAnnotation), "what is the annotation this enricher will add to the issues, by default `Enriched Licenses`")
flag.StringVar(&repoBasePath, "repoBasePath", lookupEnvOrString("REPO_BASE_PATH", ""), `the base path of the repository, this is most likely an internally set variable`)
flag.Parse()
run()
}
30 changes: 30 additions & 0 deletions components/enrichers/codeowners/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: enricher-codeowners
labels:
v1.dracon.ocurity.com/component: enricher
spec:
params:
- name: enricher-codeowners-annotation
type: string
default: ""

workspaces:
- name: source-code-ws
description: The workspace containing the source-code to scan.
steps:
- name: run-enricher
imagePullPolicy: IfNotPresent
image: ghcr.io/ocurity/dracon/components/enrichers/codeowners/image:latest
command: ["app/components/enrichers/codeowners/codeowners"]
env:
- name: READ_PATH
value: $(workspaces.source-code-ws.path)/.dracon/producers
- name: WRITE_PATH
value: "$(workspaces.source-code-ws.path)/.dracon/enrichers/codeowners"
- name: REPO_BASE_PATH
value: "$(workspaces.source-code-ws.path)/"
- name: ANNOTATION
value: "$(params.enricher-codeowners-annotation)"
2 changes: 0 additions & 2 deletions components/enrichers/depsdev/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ func addDepsDevLink(component cdx.Component) (cdx.Component, error) {

return component, nil
}

func addDepsDevInfo(component cdx.Component, annotations map[string]string) (cdx.Component, map[string]string, error) {
var depsResp Response
licenses := cdx.Licenses{}
Expand Down Expand Up @@ -254,7 +253,6 @@ func enrichIssue(i *v1.Issue) (*v1.EnrichedIssue, error) {
log.Println(err)
continue
}

// TODO(): enrich with vulnerability info whenever a consumer supports showing arbitrary properties in components
}
newComponents = append(newComponents, newComp)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/golang-migrate/migrate/v4 v4.15.1
github.com/golang/protobuf v1.5.2
github.com/google/uuid v1.3.0
github.com/hairyhenderson/go-codeowners v0.4.0
github.com/jmoiron/sqlx v1.3.5
github.com/lib/pq v1.10.5
github.com/owenrumney/go-sarif/v2 v2.1.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hairyhenderson/go-codeowners v0.4.0 h1:Wx/tRXb07sCyHeC8mXfio710Iu35uAy5KYiBdLHdv4Q=
github.com/hairyhenderson/go-codeowners v0.4.0/go.mod h1:iJgZeCt+W/GzXo5uchFCqvVHZY2T4TAIpvuVlKVkLxc=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
Expand Down
6 changes: 6 additions & 0 deletions third_party/go/github.com/hairyhenderson/go-codeowners/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
go_module(
name = "go-codeowners",
module = "github.com/hairyhenderson/go-codeowners",
version = "v0.4.0",
visibility = ["PUBLIC"],
)

0 comments on commit da3f67a

Please sign in to comment.