Skip to content

Commit

Permalink
🎉 Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
flowirtz committed Jul 3, 2024
0 parents commit 11a173a
Show file tree
Hide file tree
Showing 33 changed files with 16,036 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Dependencies
/node_modules

# Production
/build

# Generated files
.docusaurus
.cache-loader

# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Website

This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.

### Installation

```
$ yarn
```

### Local Development

```
$ yarn start
```

This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.

### Build

```
$ yarn build
```

This command generates static content into the `build` directory and can be served using any static contents hosting service.

### Deployment

Using SSH:

```
$ USE_SSH=true yarn deploy
```

Not using SSH:

```
$ GIT_USER=<Your GitHub username> yarn deploy
```

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
3 changes: 3 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};
8 changes: 8 additions & 0 deletions docs/components/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "Creating a Component",
"position": 4,
"link": {
"type": "generated-index",
"description": "5 minutes to create your own component."
}
}
47 changes: 47 additions & 0 deletions docs/components/advanced.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
title: Advanced
description: Exploring the less common problems encountered during component creation.
sidebar_position: 4
---

## Custom Docker Images

All `.go` components get built using a default Docker image. If you want to use your own image, for example, because your component has custom dependencies, you can do that too.

One good example of such a component is the [producers/ossf-scorecard component](https://github.com/ocurity/dracon/tree/main/components/producers/ossf-scorecard). Let's take a closer look at it.

In the below `Dockerfile` we using a custom base image and then are further configuring that.

```text title="dracon/components/producers/ossf-scorecard/Dockerfile"
ARG OSSF_SCORECARD_SAFETY_BASE_IMAGE
FROM gcr.io/openssf/scorecard:stable
FROM ${OSSF_SCORECARD_SAFETY_BASE_IMAGE}
COPY --from=0 /scorecard /scorecard
ENTRYPOINT ["/scorecard"]
```

In addition, a `Makefile` is placed inside the component folder, to advice Smithy on how to build the component image.

```bash title="dracon/components/producers/ossf-scorecard/Makefile"
.PHONY: component publish

CONTAINER_REPO=
DRACON_VERSION=
OSSF_SCORECARD_SAFETY_BASE_IMAGE=$(shell test -e .custom_image && cat .custom_image || echo "alpine:latest")

DOCKER=docker

component:
$(DOCKER) build --tag $(CONTAINER_REPO)/components/producers/ossf-scorecard:$(DRACON_VERSION) \
--file Dockerfile \
$$([ "${SOURCE_CODE_REPO}" != "" ] && echo "--label=org.opencontainers.image.source=${SOURCE_CODE_REPO}" ) \
--build-arg OSSF_SCORECARD_SAFETY_BASE_IMAGE=$(OSSF_SCORECARD_SAFETY_BASE_IMAGE) ../../.. 1>&2

publish:
$(DOCKER) push $(CONTAINER_REPO)/components/producers/ossf-scorecard:$(DRACON_VERSION) 1>&2
```

And that's it! Simply by adding these two files Smithy will now be able to build your component.
111 changes: 111 additions & 0 deletions docs/components/example-producer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package main

import (
"encoding/json"
"fmt"
"log"

v1 "github.com/ocurity/dracon/api/proto/v1"

"github.com/ocurity/dracon/components/producers"
)

func main() {
if err := producers.ParseFlags(); err != nil {
log.Fatal(err)
}

inFile, err := producers.ReadInFile()
if err != nil {
log.Fatal(err)
}

var results ExampleToolOut
if err := json.Unmarshal(inFile, &results); err != nil {
log.Fatal(err)
}

issues := parseIssues(&results)

if err := producers.WriteDraconOut(
"example-tool",
issues,
); err != nil {
log.Fatal(err)
}
}

func parseIssues(out *ExampleToolOut) []*v1.Issue {
issues := []*v1.Issue{}
for _, r := range out.Issues {
issues = append(issues, &v1.Issue{
Target: fmt.Sprintf("%s:%v", r.File, r.Line),
Type: r.RuleID,
Title: r.Details,
Severity: v1.Severity(v1.Severity_value[fmt.Sprintf("SEVERITY_%s", r.Severity)]),
Cvss: 0.0,
Confidence: v1.Confidence(v1.Confidence_value[fmt.Sprintf("CONFIDENCE_%s", r.Confidence)]),
Description: r.Code,
})
}
return issues
}

// ExampleToolOut represents the output of an ExampleTool run.
type ExampleToolOut struct {
Issues []ExampleToolIssue `json:"Issues"`
}

// ExampleToolIssue represents an Example Tool Result.
type ExampleToolIssue struct {
Severity string `json:"severity"`
Confidence string `json:"confidence"`
RuleID string `json:"rule_id"`
Details string `json:"details"`
File string `json:"file"`
Code string `json:"code"`
Line string `json:"line"`
Column string `json:"column"`
}

// `
// {
// "Issues": [
// {
// "severity": "MEDIUM",
// "confidence": "HIGH",
// "rule_id": "G304",
// "details": "Potential file inclusion via variable",
// "file": "/tmp/source/foo.go",
// "code": "ioutil.ReadFile(path)",
// "line": "33",
// "column": "44"
// },
// {
// "severity": "MEDIUM",
// "confidence": "HIGH",
// "rule_id": "G304",
// "details": "Potential file inclusion via variable",
// "file": "/tmp/source/foo.go",
// "code": "ioutil.ReadFile(path)",
// "line": "33",
// "column": "44"
// },
// {
// "severity": "MEDIUM",
// "confidence": "HIGH",
// "rule_id": "G304",
// "details": "Potential file inclusion via variable",
// "file": "/tmp/source/foo.go",
// "code": "ioutil.ReadFile(path)",
// "line": "33",
// "column": "44"
// }
// ],
// "Stats": {
// "files": 1,
// "lines": 60,
// "nosec": 0,
// "found": 1
// }
// }`
53 changes: 53 additions & 0 deletions docs/components/example-producer/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"encoding/json"
"testing"

v1 "github.com/ocurity/dracon/api/proto/v1"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const exampleOutput = `
{
"Issues": [
{
"severity": "MEDIUM",
"confidence": "HIGH",
"rule_id": "G304",
"details": "Potential file inclusion via variable",
"file": "/tmp/source/foo.go",
"code": "ioutil.ReadFile(path)",
"line": "33",
"column": "44"
}
],
"Stats": {
"files": 1,
"lines": 60,
"nosec": 0,
"found": 1
}
}`

func TestParseIssues(t *testing.T) {
var results ExampleToolOut
err := json.Unmarshal([]byte(exampleOutput), &results)
require.NoError(t, err)

issues := parseIssues(&results)

expectedIssue := &v1.Issue{
Target: "/tmp/source/foo.go:33",
Type: "G304",
Title: "Potential file inclusion via variable",
Severity: v1.Severity_SEVERITY_MEDIUM,
Cvss: 0.0,
Confidence: v1.Confidence_CONFIDENCE_HIGH,
Description: "ioutil.ReadFile(path)",
}

assert.Equal(t, []*v1.Issue{expectedIssue}, issues)
}
46 changes: 46 additions & 0 deletions docs/components/example-producer/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: producer-example-tool
labels:
v1.dracon.ocurity.com/component: producer
spec:
params:
- name: producer-golang-example-tool
type: array
default:
- "-r"
- "-quiet"
- "-sort"
- "-nosec"
volumes:
- name: scratch
emptyDir: {}
workspaces:
- name: output
description: The workspace containing the source-code to scan.
steps:
- name: run-example-tool
image: docker.io/example/tool:2.15.0
command: [toolcommand]
args:
- "$(params.producer-example-tool-flags[*])"
- "-fmt=json"
- "-out=/scratch/out.json"
- "-no-fail"
- "$(workspaces.output.path)/..."
volumeMounts:
- mountPath: /scratch
name: scratch

- name: produce-issues
imagePullPolicy: IfNotPresent
image: ghcr.io/ocurity/dracon/components/producers/example-tool/image:latest
command: ["/app/components/producers/example-tool/example-tool-parser"]
args:
- "-in=/scratch/out.json"
- "-out=$(workspaces.output.path)/.dracon/producers/example-tool.pb"
volumeMounts:
- mountPath: /scratch
name: scratch
14 changes: 14 additions & 0 deletions docs/components/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
sidebar_position: 1
---

# Introduction

Components are at the heart of Smithy. They're self-contained, and run a specific functionality.
Most components are written in Go, and we recommend that you write your components following this principle, to have the best support possible. Some components are written in Python as well, and in theory, you can choose any language you want.

A very basic Go component only needs to contain two files:
- `main.go`: The component logic, what gets run when your component is called
- `task.yaml`: Metadata about your component, i.e. what parameters does it accept, etc.

Next, let's dive into creating our own producer component.
29 changes: 29 additions & 0 deletions docs/components/writing-consumers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
sidebar_position: 3
---

# Consumers

A consumer is a program that parses the Dracon compatible outputs and pushes
them into arbitrary destinations. The Dracon compatible outputs from from
*producers* and *enrichers*.

***

Consumers can be written in any language that supports protobufs. We currently
have examples in Golang and Python. They are all structured in the same way:

1. Parse program arguments:
1. `in`: the dracon compatible outputs location.
2. `raw`: whether or not to use enriched results.
2. Parse all dracon compatible output files the `in` location.
3. Do arbitrary logic with issues.
4. Create a Tekton Task `task.yaml` with
`.metadata.labels["v1.dracon.ocurity.com/component"] = consumer`.

## Consumer API

For convenience, there are helper functions in the `./consumers` pkg/module for
Golang/Python.

See the godoc for more information.
Loading

0 comments on commit 11a173a

Please sign in to comment.