├── .github ├── ISSUE_TEMPLATE.md └── workflows │ ├── e2e.yml │ ├── lint.yml │ ├── publish.yml │ └── test.yml ├── .gitignore ├── .plzconfig ├── LICENSE ├── README.md ├── api └── proto │ ├── BUILD │ ├── config.proto │ ├── engine.proto │ └── issue.proto ├── build ├── defs │ ├── BUILD │ └── kustomize.build_defs └── docker │ ├── BUILD │ ├── Dockerfile-bandit │ ├── Dockerfile-base-go │ ├── Dockerfile-base-python │ ├── Dockerfile-consumer-stdout_json │ ├── Dockerfile-dracon-builder │ └── Dockerfile-spotbugs ├── cmd ├── dracon │ ├── BUILD │ ├── cmd │ │ ├── BUILD │ │ ├── cronjob.go │ │ ├── root.go │ │ ├── run.go │ │ ├── setup.go │ │ └── version.go │ └── main.go └── enricher │ ├── BUILD │ ├── Dockerfile │ └── main.go ├── common └── jira │ ├── BUILD │ ├── config.yaml │ ├── config │ ├── BUILD │ ├── config.go │ ├── config_test.go │ └── types.go │ ├── document │ ├── BUILD │ ├── document.go │ └── types.go │ └── jira │ ├── BUILD │ ├── api.go │ ├── api_test.go │ ├── apiutils.go │ └── apiutils_test.go ├── config.example.yaml ├── consumers ├── BUILD ├── __init__.py ├── consumer.go ├── consumer.py ├── consumer_test.go ├── consumer_test.py ├── defectdojo_c │ ├── BUILD │ ├── Dockerfile │ ├── defectdojo_c.py │ └── defectdojo_test.py ├── elasticsearch │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ └── main_test.go ├── examples_test.go ├── jira_c │ ├── BUILD │ ├── Dockerfile │ ├── README.md │ ├── main.go │ └── utils │ │ ├── BUILD │ │ ├── utils.go │ │ └── utils_test.go ├── slack │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ ├── types │ │ ├── BUILD │ │ └── full-document.go │ └── utils │ │ ├── BUILD │ │ ├── utils.go │ │ └── utils_test.go └── stdout_json │ ├── BUILD │ ├── Dockerfile │ └── main.go ├── docs ├── contributers │ ├── DEVELOPING.md │ └── RELEASES.md ├── designs │ └── kustomize-support.md ├── extending │ ├── extra-patches.md │ ├── writing-consumers.md │ └── writing-producers.md ├── getting-started │ ├── kind.md │ ├── minikube.md │ ├── pipelines.md │ └── tutorials │ │ ├── constructing-your-own-pipeline.md │ │ ├── creating-your-own-consumer.md │ │ ├── creating-your-own-producer.md │ │ ├── running-demo-against-private-repository.md │ │ ├── running-demo-against-public-repository.md │ │ └── running-demos.md ├── images │ └── dracon-pipeline.png └── presentations │ └── Dracon-OWASP-Presentation-export.pdf ├── enrichment_service └── configs │ └── sql │ └── migrations │ ├── 001_init.down.sql │ ├── 001_init.up.sql │ ├── 002_varchar_to_text.down.sql │ ├── 002_varchar_to_text.up.sql │ ├── 003_add_cve_column.down.sql │ ├── 003_add_cve_column.up.sql │ └── BUILD ├── examples ├── cronjobs │ └── jira-sync-cronjob.yaml ├── git-ssh.pipeline-run.yaml ├── pipelines │ ├── golang-project │ │ ├── BUILD │ │ ├── elasticsearch-consumer.yaml │ │ ├── enricher.yaml │ │ ├── git-source.yaml │ │ ├── gosec-producer.yaml │ │ ├── kustomization.yaml │ │ ├── pipeline-run.yaml │ │ └── pipeline.yaml │ ├── mixed-lang-project │ │ ├── BUILD │ │ ├── bandit-producer.yaml │ │ ├── elasticsearch-consumer.yaml │ │ ├── enricher.yaml │ │ ├── git-source.yaml │ │ ├── gosec-producer.yaml │ │ ├── kustomization.yaml │ │ ├── pipeline-run.yaml │ │ ├── pipeline.yaml │ │ └── spotbugs-producer.yaml │ └── python-project │ │ ├── BUILD │ │ ├── bandit-producer.yaml │ │ ├── elasticsearch-consumer.yaml │ │ ├── enricher.yaml │ │ ├── git-source.yaml │ │ ├── kustomization.yaml │ │ ├── pipeline-run.yaml │ │ └── pipeline.yaml └── use-secrets.task.yaml ├── pkg ├── enrichment │ ├── BUILD │ ├── db │ │ ├── BUILD │ │ ├── db.go │ │ ├── issue.go │ │ └── mock │ │ │ └── BUILD │ ├── issue.go │ └── issue_test.go ├── kubernetes │ ├── BUILD │ ├── apply.go │ └── apply_test.go ├── putil │ ├── BUILD │ ├── load.go │ └── write.go ├── template │ ├── BUILD │ ├── array-patch.go │ ├── array-patch_test.go │ ├── load.go │ ├── load_test.go │ ├── patch.go │ ├── pipeline-resource.go │ ├── pipeline.go │ └── template.go └── version │ ├── BUILD │ └── version.go ├── pleasew ├── producers ├── BUILD ├── README.md ├── __init__.py ├── dependency_check │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ └── main_test.go ├── docker_trivy │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ ├── main_test.go │ └── types │ │ ├── BUILD │ │ └── trivy-issue.go ├── elasticsearch_filebeat │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ ├── main_test.go │ └── types │ │ ├── BUILD │ │ └── elasticsearch-filebeat-issue.go ├── examples_test.go ├── golang_gosec │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ └── main_test.go ├── golang_nancy │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ ├── main_test.go │ └── types │ │ ├── BUILD │ │ └── nancy-issue.go ├── java_spotbugs │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ └── main_test.go ├── jira_producer │ ├── BUILD │ ├── Dockerfile │ ├── README.md │ ├── main.go │ └── sync │ │ ├── BUILD │ │ ├── sync.go │ │ └── sync_test.go ├── mobsf │ ├── BUILD │ ├── Dockerfile-producer-mobsf │ ├── cli.go │ ├── main.go │ ├── project.go │ └── report │ │ ├── BUILD │ │ ├── android │ │ ├── BUILD │ │ ├── android.go │ │ └── android_test.go │ │ ├── ios │ │ ├── BUILD │ │ ├── ios.go │ │ └── ios_test.go │ │ └── report.go ├── npm_audit │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ └── types │ │ ├── BUILD │ │ ├── errors.go │ │ ├── npmfullaudit │ │ ├── BUILD │ │ ├── npmfullaudit.go │ │ └── npmfullaudit_test.go │ │ ├── npmquickaudit │ │ ├── BUILD │ │ ├── npm_advisory.go │ │ ├── npm_advisory_1556 │ │ ├── npm_advisory_no_advisorydata │ │ ├── npm_advisory_not_json │ │ ├── npm_advisory_test.go │ │ ├── npmquickaudit.go │ │ └── npmquickaudit_test.go │ │ └── types.go ├── pipsafety │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ ├── main_test.go │ └── types │ │ ├── BUILD │ │ ├── safety-issue.go │ │ └── safety-issue_test.go ├── producer.go ├── producer.py ├── producer_test.go ├── producer_test.py ├── python_bandit │ ├── BUILD │ ├── Dockerfile │ ├── __init__.py │ ├── bandit.py │ ├── bandit_test.py │ ├── main.go │ └── main_test.go ├── sarif │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ └── main_test.go ├── securityhub │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ ├── main_test.go │ └── testcases │ │ ├── empty_finding.json │ │ ├── inspector_finding.json │ │ ├── no_findings.json │ │ ├── securityhub_finding.json │ │ └── unparseable.json ├── semgrep │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ ├── main_test.go │ └── types │ │ ├── BUILD │ │ └── semgrep-issue.go ├── typescript_eslint │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ ├── main_test.go │ └── types │ │ ├── BUILD │ │ └── eslint-issue.go ├── typescript_tslint │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ ├── main_test.go │ └── types │ │ ├── BUILD │ │ └── tslint-issue.go ├── yarn_audit │ ├── BUILD │ ├── Dockerfile │ ├── README.md │ ├── main.go │ └── types │ │ ├── BUILD │ │ ├── yarn-issue.go │ │ └── yarn-issue_test.go └── zap_producer │ ├── BUILD │ ├── Dockerfile │ ├── main.go │ ├── main_test.go │ └── types │ ├── BUILD │ └── zap-issue.go ├── resources ├── patches │ ├── BUILD │ ├── patch-dracon-enriched.Consumer.yaml │ ├── patch-dracon.Enricher.yaml │ ├── patch-dracon.Pipeline.yaml │ ├── patch-dracon.PipelineResource.yaml │ ├── patch-dracon.PipelineRun.yaml │ └── patch-dracon.Producer.yaml └── persistence │ ├── elasticsearch-kibana │ ├── elasticsearch.yaml │ └── kibana.yaml │ ├── enricher-db │ └── k8s.yaml │ └── minio-storage │ ├── k8s.yaml │ └── minio.conf ├── scripts ├── BUILD ├── clean-up.sh ├── development │ ├── k8s │ │ ├── BUILD │ │ ├── elasticsearch-kibana │ │ │ ├── BUILD │ │ │ ├── elasticsearch.yaml │ │ │ └── kibana.yaml │ │ ├── enricher-db │ │ │ ├── BUILD │ │ │ └── k8s.yaml │ │ ├── minio-storage │ │ │ ├── BUILD │ │ │ ├── k8s.yaml │ │ │ └── minio.conf │ │ ├── namespace.yaml │ │ └── tektoncd-dashboard │ │ │ ├── BUILD │ │ │ ├── ingress.yaml │ │ │ └── kustomization.yaml │ └── kind │ │ ├── BUILD │ │ ├── configuration.yaml │ │ ├── deploy.sh │ │ ├── dracon.sh │ │ ├── ingress │ │ ├── BUILD │ │ ├── cert-manager.yaml │ │ └── ingress-nginx.yaml │ │ ├── setup.sh │ │ └── util.sh ├── fmt │ ├── BUILD │ ├── go.sh │ └── plz.sh ├── install.sh ├── lint │ ├── BUILD │ ├── go.sh │ └── plz.sh ├── minikube.sh ├── pre-release.sh ├── publish-images.sh ├── tag-release.sh └── util │ ├── BUILD │ └── util.sh ├── source └── git │ ├── BUILD │ ├── Dockerfile │ └── git.sh ├── test └── e2e │ ├── BUILD │ └── wait_for_pipelineruns.sh ├── third_party ├── defs │ └── BUILD ├── go │ └── BUILD ├── k8s │ └── BUILD ├── lang │ └── BUILD ├── proto │ └── BUILD ├── python │ └── BUILD ├── sh │ └── BUILD └── tools │ └── BUILD └── tools └── npm_audit ├── BUILD ├── Dockerfile-tool-npm-audit ├── index.js ├── package-lock.json └── package.json /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Issue 2 | 3 | ### Expected behaviour 4 | Tell us what should happen. 5 | 6 | ### Actual behaviour 7 | Tell us what happens instead. 8 | 9 | ### Steps to reproduce 10 | Tell us how to reproduce the error. 11 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yml: -------------------------------------------------------------------------------- 1 | name: E2E 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | build: 11 | name: E2E 12 | runs-on: ubuntu-22.04 13 | strategy: 14 | matrix: 15 | kubernetes: ["1.21"] 16 | steps: 17 | - name: Install python dependencies 18 | run: sudo apt-get update && sudo apt-get install -y python3-setuptools python3-pip 19 | 20 | - name: Check out code 21 | uses: actions/checkout@v3 22 | 23 | - name: Create KinD cluster 24 | run: ./pleasew run //scripts/development/kind:setup -- --kubernetes_version="${{ matrix.kubernetes }}" 25 | 26 | - name: Deploy Dracon supporting resources 27 | run: | 28 | ./pleasew deploy //scripts/development/k8s:namespace 29 | ./pleasew deploy //scripts/development/k8s/enricher-db --wait 30 | ./pleasew deploy //scripts/development/k8s/minio-storage --wait 31 | ./pleasew deploy //scripts/development/k8s/elasticsearch-kibana --wait 32 | 33 | - name: Setup and Run Example pipelines 34 | run: | 35 | ./pleasew dracon setup --namespace dracon --pipeline examples/pipelines/golang-project 36 | ./pleasew dracon run --namespace dracon --pipeline examples/pipelines/golang-project 37 | 38 | ./pleasew dracon setup --namespace dracon --pipeline examples/pipelines/mixed-lang-project 39 | ./pleasew dracon run --namespace dracon --pipeline examples/pipelines/mixed-lang-project 40 | 41 | ./pleasew dracon setup --namespace dracon --pipeline examples/pipelines/python-project 42 | ./pleasew dracon run --namespace dracon --pipeline examples/pipelines/python-project 43 | 44 | ./pleasew run //test/e2e:wait_for_pipelineruns 45 | 46 | - name: Delete KinD cluster 47 | run: ./pleasew run //scripts/development/kind:delete 48 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | build: 11 | name: Lint 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - name: Install python dependencies 15 | run: sudo apt-get update && sudo apt-get install -y python3-setuptools python3-pip 16 | 17 | - name: Check out code 18 | uses: actions/checkout@v3 19 | 20 | - name: Lint 21 | run: ./pleasew lint 22 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | jobs: 7 | build: 8 | name: Build 9 | runs-on: ubuntu-22.04 10 | steps: 11 | - name: Install python dependencies 12 | run: sudo apt-get update && sudo apt-get install -y python3-setuptools python3-pip 13 | 14 | - name: Check out code 15 | uses: actions/checkout@v3 16 | 17 | - name: Please build 18 | run: ./pleasew build //... 19 | 20 | - name: Create pre-release 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | run: ./pleasew run //scripts:pre-release 24 | 25 | - name: Publish Docker images 26 | env: 27 | DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} 28 | DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} 29 | run: ./pleasew run //scripts:publish-images 30 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | build: 11 | name: Test 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - name: Install python dependencies 15 | run: sudo apt-get update && sudo apt-get install -y python3-setuptools python3-pip 16 | 17 | - name: Check out code 18 | uses: actions/checkout@v3 19 | 20 | - name: Build 21 | run: ./pleasew build -p -v 2 //... 22 | 23 | - name: Test 24 | run: ./pleasew test //... 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist/ 2 | /vendor/ 3 | __pycache__ 4 | *.pyc 5 | **/node_modules/ 6 | 7 | # Please output directory 8 | plz-out 9 | .plz_clean* 10 | -------------------------------------------------------------------------------- /.plzconfig: -------------------------------------------------------------------------------- 1 | ; Please config file 2 | ; Leaving this file as is is enough to use plz to build your project. 3 | ; Please will stay on whatever version you currently have until you run 4 | ; 'plz update', when it will download the latest available version. 5 | ; 6 | ; Or you can uncomment the following to pin everyone to a particular version; 7 | ; when you change it all users will automatically get updated. 8 | [please] 9 | version = 16.27.0 10 | 11 | [buildconfig] 12 | default-docker-repo = index.docker.io/thoughtmachine 13 | protoc-deps = //third_party/proto:protoc_deps 14 | kustomize-tool = //third_party/tools:kustomize 15 | 16 | [go] 17 | importpath = github.com/thought-machine/dracon 18 | gotool = //third_party/lang:go_tool|go 19 | 20 | [python] 21 | defaultinterpreter = python3 22 | moduledir = third_party.python 23 | usepypi = true 24 | disablevendorflags = true 25 | 26 | [proto] 27 | protoctool = //third_party/proto:protoc 28 | pythondep = //third_party/python:protobuf 29 | godep = //third_party/go:protobuf 30 | protocgoplugin = //third_party/go:protoc-gen-go 31 | 32 | [alias "deploy"] 33 | cmd = run //scripts/development/kind:deploy -- 34 | positionallabels = true 35 | flag = --context 36 | 37 | [alias "dracon"] 38 | cmd = run //scripts/development/kind:dracon -- 39 | flag = --pipeline 40 | 41 | [alias "lint"] 42 | cmd = run sequential --include lint //scripts/lint/... 43 | 44 | [alias "fmt-all"] 45 | cmd = run sequential --include fmt //scripts/fmt/... 46 | -------------------------------------------------------------------------------- /api/proto/BUILD: -------------------------------------------------------------------------------- 1 | proto_library( 2 | name = "v1", 3 | srcs = [ 4 | "config.proto", 5 | "engine.proto", 6 | "issue.proto", 7 | ], 8 | languages = [ 9 | "py", 10 | "go", 11 | ], 12 | visibility = ["PUBLIC"], 13 | ) 14 | -------------------------------------------------------------------------------- /api/proto/config.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package dracon.v1; 3 | option go_package = "v1"; 4 | 5 | /* WrapperArgs consists of a specific params for a producer wrapper */ 6 | message WrapperArgs { 7 | string target = 1; 8 | string output = 2; 9 | } 10 | 11 | /* Config consists of params required by a producer in order to run */ 12 | message Config { 13 | WrapperArgs wrapperArgs = 1; 14 | // specific arguments for the wrapped executable 15 | map toolArgs = 2; 16 | } 17 | -------------------------------------------------------------------------------- /api/proto/engine.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package dracon.v1; 3 | option go_package = "v1"; 4 | 5 | import "api/proto/issue.proto"; 6 | import "api/proto/config.proto"; 7 | import "google/protobuf/timestamp.proto"; 8 | 9 | /* 10 | Scan related information, unique and immutable per scan run 11 | This message is copied from LaunchToolRequest to LaunchToolResponse 12 | by each producer wrapper 13 | */ 14 | message ScanInfo { 15 | // scan unique identifier 16 | string scan_uuid = 1; 17 | // timestamp of when the scan was triggered (passed to LaunchToolResponse) 18 | google.protobuf.Timestamp scan_start_time = 2; 19 | } 20 | 21 | /* LaunchToolRequest consists of a request configuration, 22 | * built by the engine and processed by a producer, 23 | * through which options are passed */ 24 | message LaunchToolRequest { 25 | ScanInfo scan_info = 1; 26 | dracon.v1.Config config = 2; 27 | } 28 | 29 | /* LaunchToolReponse consists of a response built by a producer, 30 | * to be interpreted by a consumer */ 31 | message LaunchToolResponse { 32 | ScanInfo scan_info = 1; 33 | // The name of the tool that ran the scan 34 | string tool_name = 2; 35 | // Issues discovered during the scan 36 | repeated dracon.v1.Issue issues = 3; 37 | } 38 | 39 | /* 40 | An EnrichedLaunchToolResponse consists of deduplicated vulnerability 41 | information, with added metadata for consumers 42 | */ 43 | message EnrichedLaunchToolResponse { 44 | // The results of the original scan prior to enrichment 45 | LaunchToolResponse original_results = 1; 46 | // Enriched, deduplicated issues 47 | repeated EnrichedIssue issues = 2; 48 | } 49 | -------------------------------------------------------------------------------- /api/proto/issue.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package dracon.v1; 3 | option go_package = "v1"; 4 | 5 | import "google/protobuf/timestamp.proto"; 6 | 7 | /* Confidence represents the tool's confidence that an issue exists */ 8 | enum Confidence { 9 | CONFIDENCE_INFO = 0; 10 | CONFIDENCE_LOW = 1; 11 | CONFIDENCE_MEDIUM = 2; 12 | CONFIDENCE_HIGH = 3; 13 | CONFIDENCE_CRITICAL = 4; 14 | } 15 | 16 | /* Severity represents the reported issue severity */ 17 | enum Severity { 18 | SEVERITY_INFO = 0; 19 | SEVERITY_LOW = 1; 20 | SEVERITY_MEDIUM = 2; 21 | SEVERITY_HIGH = 3; 22 | SEVERITY_CRITICAL = 4; 23 | } 24 | 25 | /* Issue represents a vulnerability to be processed by consumers */ 26 | message Issue { 27 | string target = 1; // can be host:port or //vault/foo/bar:34-67 28 | string type = 2; // CWE-ID, etc for XSS, CSRF, etc. 29 | string title = 3; // the vulnerability title from the tool 30 | Severity severity = 4; 31 | double cvss = 5; 32 | Confidence confidence = 6; 33 | string description = 7; // human readable description of the issue 34 | string source = 8; // https://github.com/thought-machine/dracon.git?ref=, github.com:tektoncd/pipeline.git?ref=, local?ref=local 35 | string cve = 9; // [Optional] the CVE causing this vulnerability 36 | } 37 | 38 | /* Represents an issue that has been enriched with metadata from the enrichment service */ 39 | message EnrichedIssue { 40 | Issue raw_issue = 1; 41 | // The first time this issue was seen by the enrichment service 42 | google.protobuf.Timestamp first_seen = 2; 43 | // The number of times this issue was seen 44 | uint64 count = 3; 45 | // Whether this issue has been previously marked as a false positive 46 | bool false_positive = 4; 47 | // The last time this issue was updated 48 | google.protobuf.Timestamp updated_at = 5; 49 | // hash 50 | string hash = 6; 51 | } 52 | -------------------------------------------------------------------------------- /build/defs/BUILD: -------------------------------------------------------------------------------- 1 | for build_def in glob(["**.build_defs"]): 2 | export_file( 3 | name = basename(build_def).split(".")[0], 4 | src = build_def, 5 | visibility = ["PUBLIC"], 6 | ) 7 | -------------------------------------------------------------------------------- /build/docker/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | # Can be used to build Dracon if you don't want to set up a build environment 4 | docker_image( 5 | name = "dracon-builder", 6 | dockerfile = "Dockerfile-dracon-builder", 7 | image = "dracon-builder-go", 8 | ) 9 | 10 | docker_image( 11 | name = "dracon-base-go", 12 | dockerfile = "Dockerfile-base-go", 13 | image = "dracon-base-go", 14 | visibility = ["PUBLIC"], 15 | ) 16 | 17 | docker_image( 18 | name = "dracon-base-python", 19 | dockerfile = "Dockerfile-base-python", 20 | image = "dracon-base-python", 21 | visibility = ["PUBLIC"], 22 | ) 23 | 24 | docker_image( 25 | name = "bandit", 26 | dockerfile = "Dockerfile-bandit", 27 | image = "dracon-tool-bandit", 28 | ) 29 | 30 | docker_image( 31 | name = "spotbugs", 32 | dockerfile = "Dockerfile-spotbugs", 33 | image = "dracon-tool-spotbugs", 34 | ) 35 | -------------------------------------------------------------------------------- /build/docker/Dockerfile-bandit: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | RUN pip3 install bandit 4 | 5 | ENTRYPOINT ["bandit"] 6 | -------------------------------------------------------------------------------- /build/docker/Dockerfile-base-go: -------------------------------------------------------------------------------- 1 | FROM alpine:3.10 as alpine 2 | 3 | RUN apk add -U --no-cache ca-certificates libc6-compat 4 | 5 | ENTRYPOINT [] 6 | 7 | WORKDIR / 8 | -------------------------------------------------------------------------------- /build/docker/Dockerfile-base-python: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine3.10 as alpine 2 | 3 | RUN apk add -U --no-cache ca-certificates 4 | ENTRYPOINT [] 5 | WORKDIR / 6 | -------------------------------------------------------------------------------- /build/docker/Dockerfile-consumer-stdout_json: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | RUN adduser --shell /bin/false -u 1000 -D app 3 | COPY --chown=app:app . /home/app/dracon 4 | 5 | WORKDIR /home/app/dracon/ 6 | RUN pip3 install protobuf google 7 | 8 | USER app 9 | ENV PYTHONPATH=$PYTHONPATH:/home/app/dracon 10 | ENTRYPOINT ["python3","/home/app/dracon/consumers/stdout_json/stdout_json.py"] 11 | -------------------------------------------------------------------------------- /build/docker/Dockerfile-dracon-builder: -------------------------------------------------------------------------------- 1 | # Can be used to build Dracon if you don't want to set up a build environment 2 | # TODO(hjenkins) support python 3 | 4 | FROM golang:buster 5 | 6 | RUN apt-get update && \ 7 | apt-get upgrade -y && \ 8 | apt-get install -y bash curl xz-utils python3 && \ 9 | rm -rf /var/lib/apt/lists/* 10 | 11 | RUN groupadd --gid 1000 builder \ 12 | && useradd --uid 1000 --gid builder --shell /bin/bash --create-home builder 13 | ENV HOME /home/builder 14 | 15 | # This will likely be overridden by the user's flags 16 | USER builder 17 | 18 | # Install please build system 19 | RUN mkdir -p /home/builder/.config/please/ && \ 20 | curl https://get.please.build | bash 21 | ENV PATH="/home/builder/.please:${PATH}" 22 | RUN please --version 23 | RUN echo "[build]\npath = /usr/local/go/bin:/usr/local/bin:/usr/bin:/bin" > /home/builder/.config/please/plzconfig 24 | 25 | # Allow please to write to the home folder as a different user 26 | RUN chmod -R a=u /home/builder 27 | 28 | VOLUME ["/src"] 29 | WORKDIR /src 30 | -------------------------------------------------------------------------------- /build/docker/Dockerfile-spotbugs: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-alpine 2 | 3 | ARG install_dir=/opt/findsecbugs 4 | 5 | ARG SPOTBUGS_VERSION=3.1.12 6 | ARG SPOTBUGS_ADDRESS=https://repo.maven.apache.org/maven2/com/github/spotbugs/spotbugs/${SPOTBUGS_VERSION}/spotbugs-${SPOTBUGS_VERSION}.tgz 7 | ARG SPOTBUGS_SHA1=95114d9aaeeba7bd4ea5a3d6a2167cd6c87bb943 8 | 9 | ARG FINDSECBUGS_VERSION=1.10.0 10 | ARG FINDSECBUGS_ADDRESS=https://repo.maven.apache.org/maven2/com/h3xstream/findsecbugs/findsecbugs-plugin/${FINDSECBUGS_VERSION}/findsecbugs-plugin-${FINDSECBUGS_VERSION}.jar 11 | ARG FINDSECBUGS_SHA1=2ec51b4a02c65c39dbe243423d67eed7bb314dfa 12 | 13 | ENV SPOTBUGS_HOME=/opt/spotbugs 14 | RUN apk add --update curl tar bash \ 15 | # spotbugs 16 | && curl -L ${SPOTBUGS_ADDRESS} -o /tmp/spotbugs.tgz \ 17 | && echo "${SPOTBUGS_SHA1} /tmp/spotbugs.tgz" | sha1sum -c - \ 18 | && mkdir -p ${SPOTBUGS_HOME} && tar -xzf /tmp/spotbugs.tgz --strip 1 -C ${SPOTBUGS_HOME} \ 19 | && rm /tmp/spotbugs.tgz \ 20 | # findsecbugs 21 | && curl -L ${FINDSECBUGS_ADDRESS} -o ${SPOTBUGS_HOME}/plugin/findsecbugs-plugin-${FINDSECBUGS_VERSION}.jar \ 22 | && echo "${FINDSECBUGS_SHA1} ${SPOTBUGS_HOME}/plugin/findsecbugs-plugin-${FINDSECBUGS_VERSION}.jar" | sha1sum -c - \ 23 | # clean up 24 | && apk del --purge curl unzip 25 | ENV PATH="${SPOTBUGS_HOME}/bin:${PATH}" 26 | 27 | 28 | # RUN adduser --shell /bin/false -u 1000 -D app 29 | # USER app 30 | # WORKDIR /home/app/spotbugs/ 31 | ENTRYPOINT [ "spotbugs" ] 32 | -------------------------------------------------------------------------------- /cmd/dracon/BUILD: -------------------------------------------------------------------------------- 1 | go_binary( 2 | name = "dracon", 3 | srcs = [ 4 | "main.go", 5 | ], 6 | definitions = { 7 | "github.com/thought-machine/dracon/pkg/version.BuildVersion": "${SCM_REVISION}", 8 | }, 9 | stamp = True, 10 | static = True, 11 | visibility = ["//scripts/..."], 12 | deps = [ 13 | "//cmd/dracon/cmd", 14 | "//third_party/go:spf13_cobra", 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /cmd/dracon/cmd/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "cmd", 3 | srcs = [ 4 | "cronjob.go", 5 | "root.go", 6 | "run.go", 7 | "setup.go", 8 | "version.go", 9 | ], 10 | visibility = ["PUBLIC"], 11 | deps = [ 12 | "//pkg/kubernetes", 13 | "//pkg/template", 14 | "//pkg/version", 15 | "//third_party/go:mitchellh_go-homedir", 16 | "//third_party/go:spf13_cobra", 17 | "//third_party/go:spf13_viper", 18 | ], 19 | ) 20 | -------------------------------------------------------------------------------- /cmd/dracon/cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | /* 4 | Copyright © 2019 Thought Machine 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | import ( 20 | "fmt" 21 | 22 | "github.com/thought-machine/dracon/pkg/version" 23 | 24 | "github.com/spf13/cobra" 25 | ) 26 | 27 | // versionCmd represents the version command 28 | var versionCmd = &cobra.Command{ 29 | Use: "version", 30 | Short: "Print the Dracon version", 31 | Long: `Print the Dracon client and server version information for the current context`, 32 | Run: func(cmd *cobra.Command, args []string) { 33 | fmt.Printf("Version: %s\n", version.BuildVersion) 34 | }, 35 | } 36 | 37 | func init() { 38 | rootCmd.AddCommand(versionCmd) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/dracon/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2019 Thought Machine 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package main 17 | 18 | import ( 19 | "github.com/thought-machine/dracon/cmd/dracon/cmd" 20 | ) 21 | 22 | func main() { 23 | cmd.Execute() 24 | } 25 | -------------------------------------------------------------------------------- /cmd/enricher/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "enricher", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | static = True, 9 | deps = [ 10 | "//api/proto:v1", 11 | "//pkg/enrichment", 12 | "//pkg/enrichment/db", 13 | "//pkg/putil", 14 | "//third_party/go:protobuf", 15 | "//third_party/go:spf13_cobra", 16 | "//third_party/go:spf13_viper", 17 | ], 18 | ) 19 | 20 | docker_image( 21 | name = "dracon-enricher", 22 | srcs = [":enricher"], 23 | base_image = "//build/docker:dracon-base-go", 24 | image = "dracon-enricher", 25 | visibility = ["//examples/..."], 26 | ) 27 | -------------------------------------------------------------------------------- /cmd/enricher/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY enricher /enricher 4 | 5 | ENTRYPOINT ["/enricher"] 6 | -------------------------------------------------------------------------------- /common/jira/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "config_file", 3 | srcs = ["config.yaml"], 4 | visibility = ["PUBLIC"], # public visibility as both producers and consumers need access to this 5 | ) 6 | -------------------------------------------------------------------------------- /common/jira/config/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "config", 3 | srcs = [ 4 | "config.go", 5 | "types.go", 6 | ], 7 | visibility = [ 8 | "PUBLIC", 9 | ], 10 | deps = [ 11 | "//third_party/go:yaml.v2", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "config_test", 17 | srcs = [ 18 | "config_test.go", 19 | ], 20 | deps = [ 21 | ":config", 22 | "//third_party/go:stretchr_testify", 23 | "//third_party/go:yaml.v2", 24 | ], 25 | ) 26 | -------------------------------------------------------------------------------- /common/jira/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | 7 | yaml "gopkg.in/yaml.v2" 8 | ) 9 | 10 | // New reads the configuration from the file/Reader and parses it into a Config object 11 | func New(r io.Reader) (Config, error) { 12 | configBytes, err := ioutil.ReadAll(r) 13 | if err != nil { 14 | return Config{}, err 15 | } 16 | 17 | var newConfig Config 18 | err = yaml.Unmarshal(configBytes, &newConfig) 19 | if err != nil { 20 | return Config{}, err 21 | } 22 | return newConfig, nil 23 | } 24 | -------------------------------------------------------------------------------- /common/jira/config/config_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var sampleConfig = Config{ 11 | DefaultValues: DefaultValues{ 12 | Project: "TEST", 13 | IssueType: "Vulnerability", 14 | Components: []string{"c1", "c2", "c3"}, 15 | AffectsVersions: []string{"V1", "V2"}, 16 | Labels: []string(nil), 17 | CustomFields: []CustomField{{ 18 | ID: "customfield_10000", 19 | FieldType: "multi-value", 20 | Values: []string{"foo", "bar"}, 21 | }}, 22 | }, 23 | Mappings: []Mappings{{ 24 | DraconField: "cvss", 25 | JiraField: "customfield_10001", 26 | FieldType: "float", 27 | }}, 28 | DescriptionExtras: []string{"target", "tool_name"}, 29 | SyncMappings: []JiraToDraconVulnMappings{ 30 | { 31 | JiraStatus: "Test", 32 | JiraResolution: "Test", 33 | DraconStatus: "Resolved", 34 | }, 35 | }, 36 | } 37 | 38 | func TestGetConfig(t *testing.T) { 39 | testConfig := ` 40 | defaultValues: 41 | project: 'TEST' 42 | issueType: 'Vulnerability' 43 | components: ['c1', 'c2', 'c3'] 44 | affectsVersions: ['V1', 'V2'] 45 | customFields: 46 | - id: 'customfield_10000' 47 | fieldType: "multi-value" 48 | values: ['foo', 'bar'] 49 | addToDescription: 50 | - target 51 | - tool_name 52 | mappings: 53 | - draconField: "cvss" 54 | jiraField: "customfield_10001" 55 | fieldType: "float" 56 | 57 | syncMappings: 58 | - jiraStatus: "Test" 59 | jiraResolution: "Test" 60 | draconStatus: "Resolved" 61 | ` 62 | reader := strings.NewReader(testConfig) 63 | res, err := New(reader) 64 | assert.Nil(t, err) 65 | assert.EqualValues(t, res, sampleConfig) 66 | } 67 | -------------------------------------------------------------------------------- /common/jira/document/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "document", 3 | srcs = [ 4 | "document.go", 5 | "types.go", 6 | ], 7 | visibility = [ 8 | "PUBLIC", 9 | ], 10 | deps = [ 11 | "//api/proto:v1", 12 | "//third_party/go:protobuf", 13 | ], 14 | ) 15 | -------------------------------------------------------------------------------- /common/jira/document/types.go: -------------------------------------------------------------------------------- 1 | package document 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // Document represents a Dracon result (issue) object 8 | type Document struct { 9 | ScanStartTime time.Time `json:"scan_start_time"` 10 | ScanID string `json:"scan_id"` 11 | ToolName string `json:"tool_name"` 12 | Source string `json:"source"` 13 | Target string `json:"target"` 14 | Type string `json:"type"` 15 | Title string `json:"title"` 16 | SeverityText string `json:"severity_text"` 17 | CVSS string `json:"cvss"` 18 | ConfidenceText string `json:"confidence_text"` 19 | Description string `json:"description"` 20 | FirstFound time.Time `json:"first_found"` 21 | Count string `json:"count"` 22 | FalsePositive string `json:"false_positive"` 23 | Hash string `json:"hash"` 24 | CVE string `json:"cve"` 25 | // The fields below are not used in this consumer. We use the text versions instead. 26 | // Severity v1.Severity `json:"severity"` 27 | // Confidence v1.Confidence `json:"confidence"` 28 | } 29 | -------------------------------------------------------------------------------- /common/jira/jira/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "jira", 3 | srcs = [ 4 | "api.go", 5 | "apiutils.go", 6 | ], 7 | visibility = [ 8 | "PUBLIC", 9 | ], 10 | deps = [ 11 | "//common/jira/config", 12 | "//third_party/go:go-jira", 13 | "//third_party/go:tgo", 14 | ], 15 | ) 16 | 17 | go_test( 18 | name = "jira_test", 19 | srcs = [ 20 | "api_test.go", 21 | "apiutils_test.go", 22 | ], 23 | deps = [ 24 | ":jira", 25 | "//third_party/go:go-jira", 26 | "//third_party/go:stretchr_testify", 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /config.example.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thought-machine/dracon/afa59ace9a311a5ab1f171450b720b7ac054365a/config.example.yaml -------------------------------------------------------------------------------- /consumers/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "consumers", 3 | srcs = [ 4 | "consumer.go", 5 | ], 6 | visibility = ["//consumers/..."], 7 | deps = [ 8 | "//api/proto:v1", 9 | "//pkg/putil", 10 | "//third_party/go:gogo_protobuf", 11 | "//third_party/go:protobuf", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "consumers_go_test", 17 | srcs = [ 18 | "consumer.go", 19 | "consumer_test.go", 20 | ], 21 | deps = [ 22 | "//api/proto:v1", 23 | "//pkg/putil", 24 | "//third_party/go:gogo_protobuf", 25 | "//third_party/go:protobuf", 26 | "//third_party/go:stretchr_testify", 27 | ], 28 | ) 29 | 30 | python_test( 31 | name = "consumers_py_test", 32 | srcs = [ 33 | "consumer_test.py", 34 | ], 35 | deps = [ 36 | ":consumers_base_python", 37 | "//api/proto:v1", 38 | "//third_party/python:protobuf", 39 | ], 40 | ) 41 | 42 | python_binary( 43 | name = "consumers_base_python", 44 | main = "consumer.py", 45 | visibility = ["//consumers/..."], 46 | deps = [ 47 | "//third_party/python:protobuf", 48 | ], 49 | ) 50 | -------------------------------------------------------------------------------- /consumers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thought-machine/dracon/afa59ace9a311a5ab1f171450b720b7ac054365a/consumers/__init__.py -------------------------------------------------------------------------------- /consumers/consumer_test.go: -------------------------------------------------------------------------------- 1 | package consumers 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "os" 7 | "path" 8 | "testing" 9 | "time" 10 | 11 | v1 "github.com/thought-machine/dracon/api/proto/v1" 12 | 13 | "github.com/stretchr/testify/assert" 14 | "github.com/thought-machine/dracon/pkg/putil" 15 | ) 16 | 17 | func TestLoadToolResponse(t *testing.T) { 18 | tmpDir, err := ioutil.TempDir("", "dracon-test") 19 | assert.Nil(t, err) 20 | tmpFile, err := ioutil.TempFile(tmpDir, "dracon-test-*.pb") 21 | assert.Nil(t, err) 22 | defer os.Remove(tmpFile.Name()) 23 | issues := []*v1.Issue{ 24 | { 25 | Target: "/dracon/source/foobar", 26 | Title: "/dracon/source/barfoo", 27 | Description: "/dracon/source/example.yaml", 28 | }, 29 | } 30 | timestamp := time.Now().UTC().Format(time.RFC3339) 31 | scanID := "ab3d3290-cd9f-482c-97dc-ec48bdfcc4de" 32 | os.Setenv(EnvDraconStartTime, timestamp) 33 | os.Setenv(EnvDraconScanID, scanID) 34 | err = putil.WriteResults("test-tool", issues, tmpFile.Name(), scanID, timestamp) 35 | assert.Nil(t, err) 36 | 37 | log.Println(tmpDir) 38 | inResults = path.Dir(tmpDir) 39 | 40 | toolRes, err := LoadToolResponse() 41 | assert.Nil(t, err) 42 | log.Println(toolRes) 43 | 44 | assert.Equal(t, "test-tool", toolRes[0].GetToolName()) 45 | assert.Equal(t, scanID, toolRes[0].GetScanInfo().GetScanUuid()) 46 | } 47 | -------------------------------------------------------------------------------- /consumers/defectdojo_c/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | python_binary( 4 | name = "defectdojo", 5 | main = "defectdojo_c.py", 6 | visibility = ["//security/dracon/..."], 7 | deps = [ 8 | "//api/proto:v1", 9 | "//consumers:consumers_base_python", 10 | "//third_party/python:defectdojo_api", 11 | "//third_party/python:protobuf", 12 | ], 13 | ) 14 | 15 | python_test( 16 | name = "defectdojo_test", 17 | srcs = ["defectdojo_test.py"], 18 | deps = [ 19 | ":defectdojo", 20 | "//api/proto:v1", 21 | "//consumers:consumers_base_python", 22 | "//third_party/python:protobuf", 23 | ], 24 | ) 25 | 26 | docker_image( 27 | name = "dracon_consumer_defectdojo", 28 | srcs = [ 29 | ":defectdojo", 30 | ], 31 | base_image = "//build/docker:dracon-base-python", 32 | image = "dracon-consumer-defectdojo", 33 | ) 34 | -------------------------------------------------------------------------------- /consumers/defectdojo_c/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-python 2 | 3 | COPY /defectdojo.pex /consume.pex 4 | 5 | ENTRYPOINT ["/consume.pex"] 6 | -------------------------------------------------------------------------------- /consumers/elasticsearch/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "elasticsearch", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | static = True, 9 | deps = [ 10 | "//api/proto:v1", 11 | "//consumers", 12 | "//third_party/go:elastic_go-elasticsearch_v8", 13 | "//third_party/go:protobuf", 14 | ], 15 | ) 16 | 17 | go_test( 18 | name = "elasticsearch_test", 19 | srcs = [ 20 | "main.go", 21 | "main_test.go", 22 | ], 23 | deps = [ 24 | "//api/proto:v1", 25 | "//consumers", 26 | "//third_party/go:elastic_go-elasticsearch_v8", 27 | "//third_party/go:gogo_protobuf", 28 | "//third_party/go:protobuf", 29 | "//third_party/go:stretchr_testify", 30 | ], 31 | ) 32 | 33 | docker_image( 34 | name = "dracon-consumer-elasticsearch", 35 | srcs = [ 36 | ":elasticsearch", 37 | ], 38 | base_image = "//build/docker:dracon-base-go", 39 | image = "dracon-consumer-elasticsearch", 40 | visibility = ["//examples/..."], 41 | ) 42 | -------------------------------------------------------------------------------- /consumers/elasticsearch/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY elasticsearch /consume 4 | 5 | ENTRYPOINT ["/consume"] 6 | -------------------------------------------------------------------------------- /consumers/examples_test.go: -------------------------------------------------------------------------------- 1 | package consumers 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/golang/protobuf/ptypes" 7 | ) 8 | 9 | func ExampleParseFlags() { 10 | if err := ParseFlags(); err != nil { 11 | log.Fatal(err) 12 | } 13 | } 14 | 15 | func ExampleLoadToolResponse() { 16 | responses, err := LoadToolResponse() 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | for _, res := range responses { 21 | scanStartTime, _ := ptypes.Timestamp(res.GetScanInfo().GetScanStartTime()) 22 | _ = scanStartTime 23 | for _, iss := range res.GetIssues() { 24 | // Do your own logic with issues here 25 | _ = iss 26 | } 27 | } 28 | } 29 | 30 | func ExampleLoadEnrichedToolResponse() { 31 | responses, err := LoadEnrichedToolResponse() 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | for _, res := range responses { 36 | scanStartTime, _ := ptypes.Timestamp(res.GetOriginalResults().GetScanInfo().GetScanStartTime()) 37 | _ = scanStartTime 38 | for _, iss := range res.GetIssues() { 39 | // Do your own logic with issues here 40 | _ = iss 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /consumers/jira_c/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "jira_c", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//common/jira/config", 11 | "//common/jira/jira", 12 | "//consumers", 13 | "//consumers/jira_c/utils", 14 | "//third_party/go:protobuf", 15 | "//third_party/go:yaml.v2", 16 | ], 17 | ) 18 | 19 | docker_image( 20 | name = "image", 21 | srcs = [ 22 | ":jira_c", 23 | "//common/jira:config_file", 24 | ], 25 | base_image = "//build/docker:dracon-base-go", 26 | image = "dracon-consumer-jira", 27 | ) 28 | -------------------------------------------------------------------------------- /consumers/jira_c/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY jira_c /consume 4 | COPY config.yaml /config/config.yaml 5 | 6 | ENV DRACON_JIRA_CONFIG_PATH="/config/config.yaml" 7 | 8 | ENTRYPOINT ["/consume"] 9 | -------------------------------------------------------------------------------- /consumers/jira_c/utils/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "utils", 3 | srcs = [ 4 | "utils.go", 5 | ], 6 | visibility = [ 7 | "//consumers/jira_c", 8 | ], 9 | deps = [ 10 | "//api/proto:v1", 11 | "//common/jira/document", 12 | "//consumers", 13 | "//third_party/go:protobuf", 14 | ], 15 | ) 16 | 17 | go_test( 18 | name = "utils_test", 19 | srcs = [ 20 | "utils_test.go", 21 | ], 22 | deps = [ 23 | ":utils", 24 | "//api/proto:v1", 25 | "//consumers", 26 | "//third_party/go:gogo_protobuf", 27 | "//third_party/go:protobuf", 28 | "//third_party/go:stretchr_testify", 29 | ], 30 | ) 31 | -------------------------------------------------------------------------------- /consumers/slack/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "slack", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//consumers", 11 | "//consumers/slack/utils", 12 | "//third_party/go:protobuf", 13 | ], 14 | ) 15 | 16 | docker_image( 17 | name = "image", 18 | srcs = [ 19 | ":slack", 20 | ], 21 | base_image = "//build/docker:dracon-base-go", 22 | image = "dracon-consumer-slack", 23 | ) 24 | -------------------------------------------------------------------------------- /consumers/slack/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY slack /consume 4 | 5 | ENTRYPOINT ["/consume"] 6 | -------------------------------------------------------------------------------- /consumers/slack/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "full-document.go", 5 | ], 6 | visibility = [ 7 | "//consumers/slack/...", 8 | ], 9 | deps = ["//api/proto:v1"], 10 | ) 11 | -------------------------------------------------------------------------------- /consumers/slack/types/full-document.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "time" 5 | 6 | v1 "github.com/thought-machine/dracon/api/proto/v1" 7 | ) 8 | 9 | // FullDocument represents a complete slack message that will be sent if the long-format switch is set 10 | type FullDocument struct { 11 | ScanStartTime time.Time `json:"scan_start_time"` 12 | ScanID string `json:"scan_id"` 13 | ToolName string `json:"tool_name"` 14 | Source string `json:"source"` 15 | Target string `json:"target"` 16 | Type string `json:"type"` 17 | Title string `json:"title"` 18 | Severity v1.Severity `json:"severity"` 19 | CVSS float64 `json:"cvss"` 20 | Confidence v1.Confidence `json:"confidence"` 21 | Description string `json:"description"` 22 | FirstFound time.Time `json:"first_found"` 23 | Count uint64 `json:"count"` 24 | FalsePositive bool `json:"false_positive"` 25 | CVE string `json:"cve"` 26 | } 27 | -------------------------------------------------------------------------------- /consumers/slack/utils/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "utils", 3 | srcs = [ 4 | "utils.go", 5 | ], 6 | visibility = [ 7 | "//consumers/slack/...", 8 | ], 9 | deps = [ 10 | "//api/proto:v1", 11 | "//consumers/slack/types", 12 | "//third_party/go:protobuf", 13 | ], 14 | ) 15 | 16 | go_test( 17 | name = "slack_go_test", 18 | srcs = [ 19 | "utils_test.go", 20 | ], 21 | deps = [ 22 | ":utils", 23 | "//api/proto:v1", 24 | "//consumers", 25 | "//third_party/go:gogo_protobuf", 26 | "//third_party/go:protobuf", 27 | "//third_party/go:stretchr_testify", 28 | ], 29 | ) 30 | -------------------------------------------------------------------------------- /consumers/stdout_json/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "stdout_json", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//consumers", 11 | "//third_party/go:protobuf", 12 | ], 13 | ) 14 | 15 | docker_image( 16 | name = "image", 17 | srcs = [ 18 | ":stdout_json", 19 | ], 20 | base_image = "//build/docker:dracon-base-go", 21 | image = "dracon-consumer-stdout-json", 22 | ) 23 | -------------------------------------------------------------------------------- /consumers/stdout_json/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY stdout_json /consume 4 | 5 | ENTRYPOINT ["/consume"] 6 | -------------------------------------------------------------------------------- /docs/contributers/DEVELOPING.md: -------------------------------------------------------------------------------- 1 | # Developing 2 | 3 | We are using [Please](https://please.build) to build Dracon's binaries and docker containers. This abstracts away needing to understand the underling programming language or tooling. 4 | 5 | ## Running Dracon from Please 6 | 7 | To run the Dracon command with the `--help` flag, we can: 8 | 9 | ``` 10 | plz run //cmd/dracon:dracon -- --help 11 | ``` 12 | 13 | This does everything a build system should, downloading dependencies, calling the compiler then finally calling the application with your arguments. 14 | 15 | ## Building with please in docker 16 | 17 | If you don't want to install please in your development environment, there's the option of using our docker build container. 18 | 19 | For example building all artefacts (all the binaries), you can use: 20 | 21 | ```bash 22 | docker run --rm -it \ 23 | --volume "${PWD}:/src" \ 24 | --user $(id -u):$(id -g) \ 25 | thoughtmachine/dracon-builder-go:53322138724d569c4ff037dc36443bb5e0107aecd85e39b094ed8689b6cbc9dc \ 26 | plz build 27 | ``` 28 | 29 | Building the docker images is a little bit more tricky as you would need to mount the docker socket in to the container. At the moment we don't support doing this. Instead, you'll need to install please. 30 | 31 | ## Ensuring everything is building and testing 32 | ```bash 33 | plz build //... 34 | plz test //... 35 | scripts/dev-images.sh 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/contributers/RELEASES.md: -------------------------------------------------------------------------------- 1 | # Releases 2 | 3 | Dracon releases follow [Semantic Versioning](https://semver.org/) 4 | 5 | ## Creating a Release 6 | 7 | 1. Run `./pleasew run //scripts:tag-release` and follow the instructions. This will trigger a GitHub workflow that creates a pre-release with all the artifacts. 8 | 2. Once satisfied, promote the new pre-release to a release. 9 | -------------------------------------------------------------------------------- /docs/designs/kustomize-support.md: -------------------------------------------------------------------------------- 1 | # Design: Kustomize Support and Motivation 2 | 3 | [Kustomize](https://kustomize.io/) is a highly popular tool for mutating/patching Kubernetes configuration files. 4 | 5 | ## Local Development 6 | We can patch development parameters in transparently via plz. For example, `imagePullPolicy: Never` so that local clusters only use images that have been preloaded into the cluster. 7 | 8 | ## WIP: Distribution 9 | 10 | _Note_: this section is WIP and subject to changes. 11 | 12 | End users should be able to use Kustomize to modify any of our example pipelines for their own use-cases. e.g. 13 | 14 | ```yaml 15 | # ./kustomization.yaml 16 | resources: 17 | - github.com/thought-machine/dracon/examples/pipelines/golang-project?ref=v0.12.1 18 | # if the end-user is using plz, they can use a remote_file and reference the output directory instead 19 | 20 | namespace: dracon-demo 21 | commonLabels: 22 | my-org.io/team: my-team 23 | 24 | patches: 25 | - path: patches/my-repository.yaml 26 | target: 27 | group: dracon 28 | version: v1alpha1 29 | kind: PipelineResource 30 | name: "{{.RunID}}-git-github-oauth2-proxy" 31 | # note: this above patch doesn't currently read well so we may need to reconsider how we define pipelines. 32 | 33 | # ./patches/my-repository.yaml 34 | --- 35 | apiVersion: dracon/v1alpha1 36 | kind: PipelineResource 37 | metadata: 38 | name: "{{.RunID}}-git-github-oauth2-proxy" 39 | labels: {} 40 | spec: 41 | type: git 42 | params: 43 | - name: revision 44 | value: master 45 | - name: url 46 | value: https://github.com/my-org/my-repository.git 47 | ``` 48 | 49 | running `kustomize build` will yield the kustomized configuration to stdout, which can be redirected to a file. 50 | 51 | e.g. 52 | 53 | ``` 54 | $ mkdir -p modified-golang-project/ 55 | $ kustomize build > modified-golang-project/k8s_kustomized.yaml 56 | $ dracon setup --pipeline modified-golang-project 57 | $ dracon run --pipeline modified-golang-project 58 | ``` 59 | -------------------------------------------------------------------------------- /docs/extending/extra-patches.md: -------------------------------------------------------------------------------- 1 | ## Extra Patches 2 | 3 | This tutorial is still a work in progress. If it's something you need, let the maintainers know and they'll finish and publish it. 4 | -------------------------------------------------------------------------------- /docs/extending/writing-consumers.md: -------------------------------------------------------------------------------- 1 | ## Writing Consumers 2 | 3 | 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_. 4 | 5 | --- 6 | 7 | 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: 8 | 9 | 1. Parse program arguments: 10 | 1. `in`: the dracon compatible outputs location 11 | 2. `raw`: whether or not to use enriched results 12 | 2. Parse all dracon compatible output files the `in` location. 13 | 3. Do arbitrary logic with issues. 14 | 15 | ### Consumer API 16 | 17 | For convenience, there are helper functions in the `./consumers` pkg/module for Golang/Python. 18 | 19 | See the godoc for more information. 20 | -------------------------------------------------------------------------------- /docs/extending/writing-producers.md: -------------------------------------------------------------------------------- 1 | ## Writing Producers 2 | 3 | A producer is a program that parses the output of a tool and converts it into Dracon compatible output that can be used by _enrichers_ and _consumers_. 4 | 5 | --- 6 | 7 | Producers 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: 8 | 9 | 1. Parse program arguments: 10 | 1. `in`: the raw tool results file location 11 | 2. `out`: where to place the Dracon compatible output file location 12 | 2. Parse the `in` file into Protobufs (`LaunchToolResponse`) 13 | 3. Add metadata to Protobufs (e.g. git/source-code information) 14 | 4. Write the protobuf bytes to the `out` file 15 | 16 | ### Producer API 17 | 18 | For convenience, there are helper functions in the `./producers` pkg/module for Golang/Python. 19 | 20 | See the godoc for more information. 21 | -------------------------------------------------------------------------------- /docs/getting-started/kind.md: -------------------------------------------------------------------------------- 1 | ## Getting Started with Dracon on KinD 2 | 3 | ## Installation 4 | 5 | 1. Run the helper script which creates and configures a KinD cluster. 6 | 7 | ```bash 8 | $ ./pleasew run //scripts/development/kind:setup 9 | ``` 10 | 11 | 2. Create a namespace for Dracon 12 | 13 | ```bash 14 | $ ./pleasew deploy //scripts/development/k8s:namespace 15 | ``` 16 | 17 | 3. Create a DB for the Enricher 18 | 19 | ```bash 20 | $ ./pleasew deploy //scripts/development/k8s/enricher-db --wait 21 | ``` 22 | 23 | **Note**: Running postgres like this is not recommended in production, however it's great for a demo run of Dracon. You should use a properly set up and maintained Postgres instance with a secret username and password when you run Dracon in production. 24 | 25 | 4. Start Minio for ephemeral build storage 26 | 27 | ```bash 28 | $ ./pleasew deploy //scripts/development/k8s/minio-storage --wait 29 | ``` 30 | 31 | 5. Create Elasticsearch+Kibana for the Elasticsearch Consumer to push results into 32 | 33 | ```bash 34 | $ ./pleasew deploy //scripts/development/k8s/elasticsearch-kibana --wait 35 | ``` 36 | 37 | 6. Dracon is now ready to use. Check out the [Running Demos Guide](/docs/getting-started/tutorials/running-demos.md) 38 | 39 | ### Running a Pipeline 40 | 41 | To run that example pipeline you can execute: 42 | 43 | ```bash 44 | $ dracon run --context kind-dracon --namespace=dracon run --pipeline examples/pipelines/golang-project 45 | ``` 46 | 47 | _Note: make sure you have installed dracon via [Dracon - Installing](https://github.com/thought-machine/dracon#installing)_ 48 | 49 | ### Inspecting a Pipeline 50 | 51 | To see the progress of a Pipeline, you can execute: 52 | 53 | ```bash 54 | # To see the pipeline status 55 | $ kubectl -n dracon get pipelineruns 56 | # To see the pods running as part of the pipeline 57 | $ kubectl -n dracon get pod -w 58 | ``` 59 | -------------------------------------------------------------------------------- /docs/getting-started/pipelines.md: -------------------------------------------------------------------------------- 1 | ## Pipelines 2 | 3 | Dracon utilises [Tekton Pipelines](https://tekton.dev/) to execute a given Dracon Pipeline. 4 | 5 | There are example pipelines in the `./examples/pipelines` directory which are advised to be used as a base for your own Pipelines. 6 | 7 | A Pipeline directory consists of all resources required to run a Pipeline which includes: 8 | 9 | - Tekton Tasks 10 | - Dracon Producers 11 | - Dracon Consumers 12 | - Dracon Enrichers 13 | - Dracon Sources 14 | - A Tekton Pipeline 15 | - A Tekton PipelineRun 16 | - Any further Tekton PipelineResources external to Dracon resources e.g. Git Source 17 | 18 | ### Using Secrets 19 | 20 | Some tools, or even cloning your repository may require secrets. The following sections describe how to do this. 21 | 22 | #### Git+SSH Cloning 23 | 24 | An example `pipeline-run.yaml` for cloning via Git+SSH using Kubernetes Secrets is available at `./examples/git-ssh.pipeline-run.yaml` which provides the following resources: 25 | 26 | - `Secret`: To store the "secret" data such as SSH private key and trusted host public key 27 | - `ServiceAccount`: For Tasks within the Pipeline to assume this account and gain access to the secret 28 | - `PipelineResource`: Utilise Tekton's built in git resource kind to fetch git source code 29 | - `PipelineRun`: that uses the service account and git resource. 30 | 31 | #### Using Secrets in Tasks 32 | 33 | An example of changed resources to support using secrets in Tasks is available in `./examples/use-secrets.task.yaml` which provides the following resources: 34 | 35 | - `Secret`: To store the "secret" data such as SSH private key and trusted host public key 36 | - `ServiceAccount`: For Tasks within the Pipeline to assume this account and gain access to the secret 37 | - `PipelineResource`: Utilise Tekton's built in git resource kind to fetch git source code 38 | - `PipelineRun`: that uses the service account and git resource. 39 | - `Task`: to use the secret in an environment variable. 40 | -------------------------------------------------------------------------------- /docs/getting-started/tutorials/constructing-your-own-pipeline.md: -------------------------------------------------------------------------------- 1 | # Constructing Your Own Pipeline 2 | 3 | This tutorial is still a work in progress. If it's something you need, let the maintainers know and they'll finish and publish it. 4 | 5 | ![](docs/images/dracon-pipeline.png) 6 | -------------------------------------------------------------------------------- /docs/getting-started/tutorials/creating-your-own-consumer.md: -------------------------------------------------------------------------------- 1 | # Creating your own consumer 2 | 3 | This tutorial is still a work in progress. If it's something you need, let the maintainers know and they'll finish and publish it. 4 | -------------------------------------------------------------------------------- /docs/getting-started/tutorials/creating-your-own-producer.md: -------------------------------------------------------------------------------- 1 | # Creating your own producer 2 | 3 | This tutorial is still a work in progress. If it's something you need, let the maintainers know and they'll finish and publish it. 4 | -------------------------------------------------------------------------------- /docs/getting-started/tutorials/running-demo-against-public-repository.md: -------------------------------------------------------------------------------- 1 | # Running Dracon Demo Pipeline Against a Public Repository via Git+HTTPS 2 | 3 | ## Prerequisites 4 | 5 | - You have followed the [Getting Started with Minikube Guide](/docs/getting-started/minikube.md) 6 | 7 | --- 8 | 9 | ## Tutorial 10 | 11 | 1. Clone the Dracon repository. 12 | 13 | ```bash 14 | $ git clone https://github.com/thought-machine/dracon.git "${PWD}/dracon" 15 | ``` 16 | 17 | 2. Copy an example demo pipeline from the `./examples/pipelines` directory into your working directory. We have chosen `mixed-lang-project`. 18 | ```bash 19 | $ cp -r "${PWD}/dracon/examples/pipelines/mixed-lang-project" "${PWD}" 20 | ``` 21 | 3. Update the `tekton.dev/v1alpha1, PipelineResource` in `pipeline-run.yaml`: 22 | 23 | 1. Set `spec.params[0].value` to your desired git revision/branch. 24 | 2. Set `spec.params[1].value` to your desired git public git url. 25 | 26 | ```yaml 27 | --- 28 | # git+https config 29 | apiVersion: dracon/v1alpha1 30 | kind: PipelineResource 31 | metadata: 32 | name: "{{.RunID}}-git-github-oauth2_proxy" 33 | labels: {} 34 | spec: 35 | type: git 36 | params: 37 | - name: revision 38 | value: master 39 | - name: url 40 | value: https://github.com/pusher/oauth2_proxy.git 41 | ``` 42 | 43 | 4. You can now run `dracon setup` and `dracon run` with your pipeline 44 | 45 | ```bash 46 | $ dracon setup --pipeline mixed-lang-project 47 | $ dracon run --pipeline mixed-lang-project 48 | ``` 49 | -------------------------------------------------------------------------------- /docs/getting-started/tutorials/running-demos.md: -------------------------------------------------------------------------------- 1 | # Dracon Demos 2 | 3 | Example of running the the [Demo Dracon Pipelines](examples/pipelines/golang-project) where you get to see the results in kibana at the end. 4 | 5 | ## Prerequisites 6 | 7 | - You have followed the [Getting Started with Minikube Guide](/docs/getting-started/minikube.md) 8 | 9 | --- 10 | 11 | ## Tutorial 12 | 13 | 1. You can run a demo pipeline with: 14 | 15 | ```bash 16 | $ dracon setup --pipeline examples/pipelines/golang-project 17 | $ dracon run --pipeline examples/pipelines/golang-project 18 | ``` 19 | 20 | 2. Wait for the pipeline to finish running: 21 | 22 | ```bash 23 | $ kubectl get pipelineruns --watch 24 | ``` 25 | 26 | Note: Use ctrl-c to exit the blocking watch 27 | 28 | 3. Once the pipelinerun has finished running you can view your results in Kibana (backed by Elasticsearch): 29 | 30 | ```bash 31 | $ kubectl port-forward svc/kibana 5601 32 | ``` 33 | 34 | Visit http://localhost:5601, and create the dracon index filter 35 | -------------------------------------------------------------------------------- /docs/images/dracon-pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thought-machine/dracon/afa59ace9a311a5ab1f171450b720b7ac054365a/docs/images/dracon-pipeline.png -------------------------------------------------------------------------------- /docs/presentations/Dracon-OWASP-Presentation-export.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thought-machine/dracon/afa59ace9a311a5ab1f171450b720b7ac054365a/docs/presentations/Dracon-OWASP-Presentation-export.pdf -------------------------------------------------------------------------------- /enrichment_service/configs/sql/migrations/001_init.down.sql: -------------------------------------------------------------------------------- 1 | -- do nothing. Normally we don't drop tables unless data has been moved 2 | -------------------------------------------------------------------------------- /enrichment_service/configs/sql/migrations/001_init.up.sql: -------------------------------------------------------------------------------- 1 | -- issues table 2 | CREATE TABLE IF NOT EXISTS issues ( 3 | -- protobuf: enriched issue 4 | "hash" VARCHAR(32) NOT NULL, 5 | source VARCHAR(512) NOT NULL, 6 | first_seen TIMESTAMP WITH TIME ZONE, 7 | occurrences INTEGER NOT NULL, 8 | false_positive BOOLEAN NOT NULL, 9 | updated_at TIMESTAMP WITH TIME ZONE, 10 | -- protobuf: issue 11 | "target" VARCHAR(512) NOT NULL, 12 | "type" VARCHAR(128) NOT NULL, 13 | "title" VARCHAR(512) NOT NULL, 14 | severity INTEGER NOT NULL, 15 | cvss FLOAT NOT NULL, 16 | confidence INTEGER NOT NULL, 17 | "description" TEXT NOT NULL, 18 | PRIMARY KEY ("hash") 19 | ); 20 | CREATE INDEX idx_issues_source ON issues(source); 21 | -------------------------------------------------------------------------------- /enrichment_service/configs/sql/migrations/002_varchar_to_text.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE issues ALTER COLUMN source TYPE VARCHAR(512); 2 | ALTER TABLE issues ALTER COLUMN target TYPE VARCHAR(512); 3 | ALTER TABLE issues ALTER COLUMN title TYPE VARCHAR(512); 4 | -------------------------------------------------------------------------------- /enrichment_service/configs/sql/migrations/002_varchar_to_text.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE issues ALTER COLUMN source TYPE text; 2 | ALTER TABLE issues ALTER COLUMN target TYPE text; 3 | ALTER TABLE issues ALTER COLUMN title TYPE text; 4 | -------------------------------------------------------------------------------- /enrichment_service/configs/sql/migrations/003_add_cve_column.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE issues DROP COLUMN cve; -------------------------------------------------------------------------------- /enrichment_service/configs/sql/migrations/003_add_cve_column.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE issues ADD COLUMN cve text; -------------------------------------------------------------------------------- /enrichment_service/configs/sql/migrations/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "migrations", 3 | srcs = glob(["*.sql"]), 4 | visibility = ["//pkg/enrichment/db/..."], 5 | ) 6 | -------------------------------------------------------------------------------- /examples/cronjobs/jira-sync-cronjob.yaml: -------------------------------------------------------------------------------- 1 | # requires configuration loaded as config map, you can do so with 2 | # kubectl create configmap jira-config --from-file=, 3 | # example config file is in common/jira/config.yaml same config file works 4 | # for both jira producer and consumer 5 | --- 6 | apiVersion: batch/v1beta1 7 | kind: CronJob 8 | metadata: 9 | name: hello 10 | spec: 11 | schedule: "0 * * * *" # every hour 12 | jobTemplate: 13 | spec: 14 | template: 15 | spec: 16 | containers: 17 | - name: dracon-sync 18 | image: thoughtmachine/dracon-producer-jira 19 | env: 20 | - name: DRACON_SYNC_TOKEN 21 | value: "" 22 | - name: DRACON_SYNC_USER 23 | value: "" 24 | - name: DRACON_SYNC_DBCON 25 | value: "" 26 | volumeMounts: 27 | - name: jira-config 28 | mountPath: /etc/jira/ 29 | readOnly: true 30 | args: 31 | - --dryRun # remove for actual syncing 32 | - --jira 33 | - "" 34 | - --query 35 | - ', usually something like "filter=1234 AND issue.type=Vulnerability"' 36 | - --config 37 | - /etc/jira/config.yaml 38 | restartPolicy: OnFailure 39 | volumes: 40 | - name: jira-config 41 | configMap: 42 | name: jira-config 43 | -------------------------------------------------------------------------------- /examples/pipelines/golang-project/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//build/defs:kustomize") 2 | 3 | kustomized_config( 4 | name = "dev", 5 | srcs = [ 6 | "elasticsearch-consumer.yaml", 7 | "enricher.yaml", 8 | "git-source.yaml", 9 | "gosec-producer.yaml", 10 | "kustomization.yaml", 11 | "pipeline.yaml", 12 | "pipeline-run.yaml", 13 | ], 14 | images = [ 15 | "//consumers/elasticsearch:dracon-consumer-elasticsearch", 16 | "//cmd/enricher:dracon-enricher", 17 | "//source/git:dracon-source-git", 18 | "//producers/golang_gosec:dracon-producer-gosec", 19 | ], 20 | ) 21 | -------------------------------------------------------------------------------- /examples/pipelines/golang-project/elasticsearch-consumer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Consumer 4 | metadata: 5 | name: golang-project-elasticsearch-consumer 6 | labels: {} 7 | spec: 8 | inputs: {resources: [], params: []} 9 | steps: 10 | # run elasticsearch consumer 11 | - name: run-elasticsearch-consumer 12 | image: index.docker.io/thoughtmachine/dracon-consumer-elasticsearch:latest 13 | env: [] 14 | command: ["/consume"] 15 | args: [ 16 | "-in", "{{.ConsumerSourcePath}}", 17 | "-es-urls", "http://elasticsearch.dracon.svc:9200", 18 | "-es-index", "dracon" 19 | ] 20 | -------------------------------------------------------------------------------- /examples/pipelines/golang-project/enricher.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Enricher 4 | metadata: 5 | name: golang-project-enricher 6 | labels: {} 7 | spec: 8 | inputs: {resources: []} 9 | outputs: {resources: []} 10 | steps: 11 | # run enricher 12 | - name: run-enricher 13 | image: index.docker.io/thoughtmachine/dracon-enricher:latest 14 | env: 15 | - name: ENRICHER_READ_PATH 16 | value: /workspace 17 | - name: ENRICHER_WRITE_PATH 18 | value: "{{.EnricherOutPath}}" 19 | - name: ENRICHER_DB_CONNECTION 20 | value: "postgresql://dracon:dracon@dracon-enrichment-db.dracon.svc?sslmode=disable" 21 | command: ["/enricher"] 22 | -------------------------------------------------------------------------------- /examples/pipelines/golang-project/git-source.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: tekton.dev/v1alpha1 3 | kind: Task 4 | metadata: 5 | name: golang-project-git-source 6 | labels: {} 7 | spec: 8 | inputs: {resources: [{name: git-source, type: git}]} 9 | outputs: {resources: [{name: source, type: storage}]} 10 | steps: 11 | - name: git-source 12 | image: index.docker.io/thoughtmachine/dracon-source-git:latest 13 | command: ["/git.sh"] 14 | -------------------------------------------------------------------------------- /examples/pipelines/golang-project/gosec-producer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Producer 4 | metadata: 5 | name: golang-project-gosec-producer 6 | labels: {} 7 | spec: 8 | inputs: {resources: [], params: []} 9 | outputs: {resources: []} 10 | volumes: [] 11 | steps: 12 | # run gosec 13 | - name: run-gosec 14 | image: securego/gosec 15 | command: ["sh"] 16 | args: ["-c", 17 | "gosec -fmt=json -out={{.ProducerToolOutPath}} {{.ProducerSourcePath}}/... || true" 18 | ] 19 | volumeMounts: [] 20 | env: [] 21 | # parse results 22 | - name: parse-gosec 23 | image: index.docker.io/thoughtmachine/dracon-producer-gosec:latest 24 | command: ["/parse"] 25 | args: [ 26 | "-in={{.ProducerToolOutPath}}", 27 | "-out={{.ProducerOutPath}}" 28 | ] 29 | volumeMounts: [] 30 | env: [] 31 | -------------------------------------------------------------------------------- /examples/pipelines/golang-project/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | 5 | namespace: dracon 6 | 7 | commonLabels: 8 | app: dracon 9 | app.kubernetes.io/component: "pipeline" 10 | dracon.thoughtmachine.io/pipeline: "golang-project" 11 | 12 | resources: 13 | - elasticsearch-consumer.yaml 14 | - enricher.yaml 15 | - git-source.yaml 16 | - gosec-producer.yaml 17 | - pipeline-run.yaml 18 | - pipeline.yaml 19 | -------------------------------------------------------------------------------- /examples/pipelines/golang-project/pipeline-run.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # git+https config 3 | apiVersion: dracon/v1alpha1 4 | kind: PipelineResource 5 | metadata: 6 | name: "{{.RunID}}-git-github-oauth2-proxy" 7 | labels: {} 8 | spec: 9 | type: git 10 | params: 11 | - name: revision 12 | value: master 13 | - name: url 14 | value: https://github.com/pusher/oauth2_proxy.git 15 | --- 16 | # pipelinerun 17 | apiVersion: dracon/v1alpha1 18 | kind: PipelineRun 19 | metadata: 20 | name: "golang-project-{{.RunID}}" 21 | labels: 22 | project: dracon 23 | spec: 24 | pipelineRef: {name: golang-project} 25 | params: [] 26 | podTemplate: {} 27 | # serviceAccount: git-github-oauth2-proxy # replace with desired git source service account (if needed) 28 | resources: 29 | - {name: git-source, resourceRef: {name: "{{.RunID}}-git-github-oauth2-proxy"}} # replace with desired git source 30 | timeout: 1h0m0s 31 | -------------------------------------------------------------------------------- /examples/pipelines/golang-project/pipeline.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Pipeline 4 | metadata: 5 | name: golang-project 6 | labels: {} 7 | spec: 8 | params: [] 9 | resources: 10 | - {type: git, name: git-source} 11 | tasks: 12 | # stage 1 - archive source 13 | - name: fetch-source 14 | taskRef: {name: golang-project-git-source} 15 | resources: 16 | inputs: [{name: git-source, resource: git-source}] 17 | outputs: [] 18 | # stage 2 - run tools 19 | - name: gosec-producer 20 | runAfter: [fetch-source] 21 | taskRef: {name: golang-project-gosec-producer} 22 | params: [] 23 | resources: {inputs: [], outputs: []} 24 | # stage 3 - enrichment 25 | - name: enricher 26 | runAfter: [gosec-producer] 27 | taskRef: {name: golang-project-enricher} 28 | resources: {inputs: [], outputs: []} 29 | # stage 4 - consumers 30 | - name: elasticsearch-consumer 31 | runAfter: [enricher] 32 | taskRef: {name: golang-project-elasticsearch-consumer} 33 | params: [] 34 | resources: {inputs: [], outputs: []} 35 | -------------------------------------------------------------------------------- /examples/pipelines/mixed-lang-project/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//build/defs:kustomize") 2 | 3 | kustomized_config( 4 | name = "dev", 5 | srcs = [ 6 | "bandit-producer.yaml", 7 | "elasticsearch-consumer.yaml", 8 | "enricher.yaml", 9 | "git-source.yaml", 10 | "gosec-producer.yaml", 11 | "kustomization.yaml", 12 | "pipeline.yaml", 13 | "pipeline-run.yaml", 14 | "spotbugs-producer.yaml", 15 | ], 16 | images = [ 17 | "//consumers/elasticsearch:dracon-consumer-elasticsearch", 18 | "//cmd/enricher:dracon-enricher", 19 | "//source/git:dracon-source-git", 20 | "//producers/golang_gosec:dracon-producer-gosec", 21 | "//producers/python_bandit:dracon-producer-bandit", 22 | "//producers/java_spotbugs:dracon-producer-java_spotbugs", 23 | ], 24 | ) 25 | -------------------------------------------------------------------------------- /examples/pipelines/mixed-lang-project/bandit-producer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Producer 4 | metadata: 5 | name: mixed-lang-project-bandit-producer 6 | labels: {} 7 | spec: 8 | inputs: {resources: [], params: []} 9 | outputs: {resources: []} 10 | volumes: [] 11 | steps: 12 | # run bandit 13 | - name: run-bandit 14 | image: index.docker.io/thoughtmachine/dracon-tool-bandit:latest 15 | command: ["sh"] 16 | args: ["-c", 17 | "bandit --recursive {{.ProducerSourcePath}} --format json --output {{.ProducerToolOutPath}} || true" 18 | ] 19 | volumeMounts: [] 20 | env: [] 21 | # parse results 22 | - name: parse-bandit 23 | image: index.docker.io/thoughtmachine/dracon-producer-bandit:latest 24 | command: ["/parse"] 25 | args: [ 26 | "-in={{.ProducerToolOutPath}}", 27 | "-out={{.ProducerOutPath}}" 28 | ] 29 | volumeMounts: [] 30 | env: [] 31 | -------------------------------------------------------------------------------- /examples/pipelines/mixed-lang-project/elasticsearch-consumer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Consumer 4 | metadata: 5 | name: mixed-lang-project-elasticsearch-consumer 6 | labels: {} 7 | spec: 8 | inputs: {resources: [], params: []} 9 | steps: 10 | # run elasticsearch consumer 11 | - name: run-elasticsearch-consumer 12 | image: index.docker.io/thoughtmachine/dracon-consumer-elasticsearch:latest 13 | env: [] 14 | command: ["/consume"] 15 | args: [ 16 | "-in", "{{.ConsumerSourcePath}}", 17 | "-es-urls", "http://elasticsearch.dracon.svc:9200", 18 | "-es-index", "dracon" 19 | ] 20 | -------------------------------------------------------------------------------- /examples/pipelines/mixed-lang-project/enricher.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Enricher 4 | metadata: 5 | name: mixed-lang-project-enricher 6 | labels: {} 7 | spec: 8 | inputs: {resources: []} 9 | outputs: {resources: []} 10 | steps: 11 | # run enricher 12 | - name: run-enricher 13 | image: index.docker.io/thoughtmachine/dracon-enricher:latest 14 | env: 15 | - name: ENRICHER_READ_PATH 16 | value: /workspace 17 | - name: ENRICHER_WRITE_PATH 18 | value: "{{.EnricherOutPath}}" 19 | - name: ENRICHER_DB_CONNECTION 20 | value: "postgresql://dracon:dracon@dracon-enrichment-db.dracon.svc?sslmode=disable" 21 | command: ["/enricher"] 22 | -------------------------------------------------------------------------------- /examples/pipelines/mixed-lang-project/git-source.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: tekton.dev/v1alpha1 3 | kind: Task 4 | metadata: 5 | name: mixed-lang-project-git-source 6 | labels: {} 7 | spec: 8 | inputs: {resources: [{name: git-source, type: git}]} 9 | outputs: {resources: [{name: source, type: storage}]} 10 | steps: 11 | - name: git-source 12 | image: index.docker.io/thoughtmachine/dracon-source-git:latest 13 | command: ["/git.sh"] 14 | -------------------------------------------------------------------------------- /examples/pipelines/mixed-lang-project/gosec-producer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Producer 4 | metadata: 5 | name: mixed-lang-project-gosec-producer 6 | labels: {} 7 | spec: 8 | inputs: {resources: [], params: []} 9 | outputs: {resources: []} 10 | volumes: [] 11 | steps: 12 | # run gosec 13 | - name: run-gosec 14 | image: securego/gosec 15 | command: ["sh"] 16 | args: ["-c", 17 | "gosec -fmt=json -out={{.ProducerToolOutPath}} {{.ProducerSourcePath}}/... || true" 18 | ] 19 | volumeMounts: [] 20 | env: [] 21 | # parse results 22 | - name: parse-gosec 23 | image: index.docker.io/thoughtmachine/dracon-producer-gosec:latest 24 | imagePullPolicy: Never 25 | command: ["/parse"] 26 | args: [ 27 | "-in={{.ProducerToolOutPath}}", 28 | "-out={{.ProducerOutPath}}" 29 | ] 30 | volumeMounts: [] 31 | env: [] 32 | -------------------------------------------------------------------------------- /examples/pipelines/mixed-lang-project/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | 5 | namespace: dracon 6 | 7 | commonLabels: 8 | app: dracon 9 | app.kubernetes.io/component: "pipeline" 10 | dracon.thoughtmachine.io/pipeline: "mixed-lang-project" 11 | 12 | resources: 13 | - elasticsearch-consumer.yaml 14 | - enricher.yaml 15 | - git-source.yaml 16 | - gosec-producer.yaml 17 | - bandit-producer.yaml 18 | - spotbugs-producer.yaml 19 | - pipeline-run.yaml 20 | - pipeline.yaml 21 | -------------------------------------------------------------------------------- /examples/pipelines/mixed-lang-project/pipeline-run.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # git+https config 3 | apiVersion: dracon/v1alpha1 4 | kind: PipelineResource 5 | metadata: 6 | name: "{{.RunID}}-git-github-thoughtmachine-please" 7 | labels: {} 8 | spec: 9 | type: git 10 | params: 11 | - name: revision 12 | value: master 13 | - name: url 14 | value: https://github.com/thought-machine/please.git 15 | --- 16 | # pipelinerun 17 | apiVersion: dracon/v1alpha1 18 | kind: PipelineRun 19 | metadata: 20 | name: "mixed-lang-project-{{.RunID}}" 21 | labels: 22 | project: dracon 23 | spec: 24 | pipelineRef: {name: mixed-lang-project} 25 | params: [] 26 | podTemplate: {} 27 | # serviceAccount: git-github-thoughtmachine-please # replace with desired git source service account (if needed) 28 | resources: 29 | - {name: git-source, resourceRef: {name: "{{.RunID}}-git-github-thoughtmachine-please"}} # replace with desired git source 30 | timeout: 1h0m0s 31 | -------------------------------------------------------------------------------- /examples/pipelines/mixed-lang-project/pipeline.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Pipeline 4 | metadata: 5 | name: mixed-lang-project 6 | labels: {} 7 | spec: 8 | params: [] 9 | resources: 10 | - {type: git, name: git-source} 11 | tasks: 12 | # stage 1 - archive source 13 | - name: fetch-source 14 | taskRef: {name: mixed-lang-project-git-source} 15 | resources: 16 | inputs: [{name: git-source, resource: git-source}] 17 | outputs: [] 18 | # stage 2 - run tools 19 | - name: bandit-producer 20 | runAfter: [fetch-source] 21 | taskRef: {name: mixed-lang-project-bandit-producer} 22 | params: [] 23 | resources: {inputs: [], outputs: []} 24 | - name: gosec-producer 25 | runAfter: [fetch-source] 26 | taskRef: {name: mixed-lang-project-gosec-producer} 27 | params: [] 28 | resources: {inputs: [], outputs: []} 29 | # stage 3 - enrichment 30 | - name: enricher 31 | runAfter: [bandit-producer, gosec-producer] 32 | taskRef: {name: mixed-lang-project-enricher} 33 | resources: {inputs: [], outputs: []} 34 | # stage 4 - consumers 35 | - name: elasticsearch-consumer 36 | runAfter: [enricher] 37 | taskRef: {name: mixed-lang-project-elasticsearch-consumer} 38 | params: [] 39 | resources: {inputs: [], outputs: []} 40 | -------------------------------------------------------------------------------- /examples/pipelines/mixed-lang-project/spotbugs-producer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Producer 4 | metadata: 5 | name: mixed-lang-project-spotbugs-producer 6 | labels: {} 7 | spec: 8 | inputs: {resources: [], params: []} 9 | outputs: {resources: []} 10 | volumes: [] 11 | steps: 12 | # run spotbugs 13 | - name: run-spotbugs 14 | image: index.docker.io/thoughtmachine/dracon-tool-spotbugs:latest 15 | command: ["sh"] 16 | args: ["-c", 17 | "spotbugs {{.ProducerSourcePath}} -output {{.ProducerToolOutPath}} -xml:withMessages" 18 | ] 19 | volumeMounts: [] 20 | env: [] 21 | # parse results 22 | - name: parse-spotbugs 23 | image: index.docker.io/thoughtmachine/dracon-producer-java_spotbugs:latest 24 | imagePullPolicy: Never 25 | command: ["/parse"] 26 | args: [ 27 | "-in={{.ProducerToolOutPath}}", 28 | "-out={{.ProducerOutPath}}" 29 | ] 30 | volumeMounts: [] 31 | env: [] 32 | -------------------------------------------------------------------------------- /examples/pipelines/python-project/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//build/defs:kustomize") 2 | 3 | kustomized_config( 4 | name = "dev", 5 | srcs = [ 6 | "bandit-producer.yaml", 7 | "elasticsearch-consumer.yaml", 8 | "enricher.yaml", 9 | "git-source.yaml", 10 | "kustomization.yaml", 11 | "pipeline.yaml", 12 | "pipeline-run.yaml", 13 | ], 14 | images = [ 15 | "//consumers/elasticsearch:dracon-consumer-elasticsearch", 16 | "//cmd/enricher:dracon-enricher", 17 | "//source/git:dracon-source-git", 18 | "//producers/python_bandit:dracon-producer-bandit", 19 | ], 20 | ) 21 | -------------------------------------------------------------------------------- /examples/pipelines/python-project/bandit-producer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Producer 4 | metadata: 5 | name: python-project-bandit-producer 6 | labels: {} 7 | spec: 8 | inputs: {resources: [], params: []} 9 | outputs: {resources: []} 10 | volumes: [] 11 | steps: 12 | # run bandit 13 | - name: run-bandit 14 | image: index.docker.io/thoughtmachine/dracon-tool-bandit:latest 15 | command: ["sh"] 16 | args: ["-c", 17 | "bandit --recursive {{.ProducerSourcePath}} --format json --output {{.ProducerToolOutPath}} || true" 18 | ] 19 | volumeMounts: [] 20 | env: [] 21 | # parse results 22 | - name: parse-bandit 23 | image: index.docker.io/thoughtmachine/dracon-producer-bandit:latest 24 | command: ["/parse"] 25 | args: [ 26 | "-in={{.ProducerToolOutPath}}", 27 | "-out={{.ProducerOutPath}}" 28 | ] 29 | volumeMounts: [] 30 | env: [] 31 | -------------------------------------------------------------------------------- /examples/pipelines/python-project/elasticsearch-consumer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Consumer 4 | metadata: 5 | name: python-project-elasticsearch-consumer 6 | labels: {} 7 | spec: 8 | inputs: {resources: [], params: []} 9 | steps: 10 | # run elasticsearch consumer 11 | - name: run-elasticsearch-consumer 12 | image: index.docker.io/thoughtmachine/dracon-consumer-elasticsearch:latest 13 | imagePullPolicy: Never 14 | env: [] 15 | command: ["/consume"] 16 | args: [ 17 | "-in", "{{.ConsumerSourcePath}}", 18 | "-es-urls", "http://elasticsearch.dracon.svc:9200", 19 | "-es-index", "dracon" 20 | ] 21 | -------------------------------------------------------------------------------- /examples/pipelines/python-project/enricher.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Enricher 4 | metadata: 5 | name: python-project-enricher 6 | labels: {} 7 | spec: 8 | inputs: {resources: []} 9 | outputs: {resources: []} 10 | steps: 11 | # run enricher 12 | - name: run-enricher 13 | image: index.docker.io/thoughtmachine/dracon-enricher:latest 14 | imagePullPolicy: Never 15 | env: 16 | - name: ENRICHER_READ_PATH 17 | value: /workspace 18 | - name: ENRICHER_WRITE_PATH 19 | value: "{{.EnricherOutPath}}" 20 | - name: ENRICHER_DB_CONNECTION 21 | value: "postgresql://dracon:dracon@dracon-enrichment-db.dracon.svc?sslmode=disable" 22 | command: ["/enricher"] 23 | -------------------------------------------------------------------------------- /examples/pipelines/python-project/git-source.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: tekton.dev/v1alpha1 3 | kind: Task 4 | metadata: 5 | name: python-project-git-source 6 | labels: {} 7 | spec: 8 | inputs: {resources: [{name: git-source, type: git}]} 9 | outputs: {resources: [{name: source, type: storage}]} 10 | steps: 11 | - name: git-source 12 | image: index.docker.io/thoughtmachine/dracon-source-git:latest 13 | imagePullPolicy: Never 14 | command: ["/git.sh"] 15 | -------------------------------------------------------------------------------- /examples/pipelines/python-project/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | 5 | namespace: dracon 6 | 7 | commonLabels: 8 | app: dracon 9 | app.kubernetes.io/component: "pipeline" 10 | dracon.thoughtmachine.io/pipeline: "python-project" 11 | 12 | resources: 13 | - elasticsearch-consumer.yaml 14 | - enricher.yaml 15 | - git-source.yaml 16 | - bandit-producer.yaml 17 | - pipeline-run.yaml 18 | - pipeline.yaml 19 | -------------------------------------------------------------------------------- /examples/pipelines/python-project/pipeline-run.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # git+https config 3 | apiVersion: dracon/v1alpha1 4 | kind: PipelineResource 5 | metadata: 6 | name: "{{.RunID}}-git-github-httpie-httpie" 7 | labels: {} 8 | spec: 9 | type: git 10 | params: 11 | - name: revision 12 | value: master 13 | - name: url 14 | value: https://github.com/httpie/httpie.git 15 | --- 16 | # pipelinerun 17 | apiVersion: dracon/v1alpha1 18 | kind: PipelineRun 19 | metadata: 20 | name: "python-project-{{.RunID}}" 21 | labels: 22 | project: dracon 23 | spec: 24 | pipelineRef: {name: python-project} 25 | params: [] 26 | podTemplate: {} 27 | # serviceAccount: git-github-httpie-httpie # replace with desired git source service account (if needed) 28 | resources: 29 | - {name: git-source, resourceRef: {name: "{{.RunID}}-git-github-httpie-httpie"}} # replace with desired git source 30 | timeout: 1h0m0s 31 | -------------------------------------------------------------------------------- /examples/pipelines/python-project/pipeline.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: dracon/v1alpha1 3 | kind: Pipeline 4 | metadata: 5 | name: python-project 6 | labels: {} 7 | spec: 8 | params: [] 9 | resources: 10 | - {type: git, name: git-source} 11 | tasks: 12 | # stage 1 - archive source 13 | - name: fetch-source 14 | taskRef: {name: python-project-git-source} 15 | resources: 16 | inputs: [{name: git-source, resource: git-source}] 17 | outputs: [] 18 | # stage 2 - run tools 19 | - name: bandit-producer 20 | runAfter: [fetch-source] 21 | taskRef: {name: python-project-bandit-producer} 22 | params: [] 23 | resources: {inputs: [], outputs: []} 24 | # stage 3 - enrichment 25 | - name: enricher 26 | runAfter: [bandit-producer] 27 | taskRef: {name: python-project-enricher} 28 | resources: {inputs: [], outputs: []} 29 | # stage 4 - consumers 30 | - name: elasticsearch-consumer 31 | runAfter: [enricher] 32 | taskRef: {name: python-project-elasticsearch-consumer} 33 | params: [] 34 | resources: {inputs: [], outputs: []} 35 | -------------------------------------------------------------------------------- /examples/use-secrets.task.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: my-secret-consumer 6 | labels: {} 7 | type: kubernetes.io/ssh-auth 8 | data: 9 | # echo "FOO" | base64 -w 0 10 | MY_SECRET: Rk9PCg== 11 | --- 12 | apiVersion: v1 13 | kind: ServiceAccount 14 | metadata: 15 | name: my-secret-consumer 16 | labels: {} 17 | secrets: 18 | - name: my-secret-consumer 19 | --- 20 | apiVersion: dracon/v1alpha1 21 | kind: Consumer 22 | metadata: 23 | name: secrets-consumer 24 | labels: {} 25 | spec: 26 | inputs: {resources: [], params: []} 27 | steps: 28 | # run secrets consumer 29 | - name: run-secrets-consumer 30 | image: dracon/consumer/secrets:latest 31 | imagePullPolicy: Never 32 | env: [ 33 | {name: MY_SECRET, valueFrom: {secretKeyRef: MY_SECRET}} 34 | ] 35 | command: ["/consume"] 36 | args: [ 37 | "-in", "{{.ConsumerSourcePath}}", 38 | ] 39 | --- 40 | # pipelinerun 41 | apiVersion: tekton.dev/v1alpha1 42 | kind: PipelineRun 43 | metadata: 44 | name: "demo-secrets-{{.RunID}}" 45 | labels: {} 46 | spec: 47 | pipelineRef: {name: demo} 48 | params: [] 49 | podTemplate: {} 50 | serviceAccountNames: 51 | - my-secret-consumer 52 | resources: [] 53 | timeout: 1h0m0s 54 | -------------------------------------------------------------------------------- /pkg/enrichment/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "enrichment", 3 | srcs = [ 4 | "issue.go", 5 | ], 6 | visibility = ["PUBLIC"], 7 | deps = [ 8 | "//api/proto:v1", 9 | "//pkg/enrichment/db", 10 | "//third_party/go:protobuf", 11 | ], 12 | ) 13 | 14 | go_test( 15 | name = "enrichment_test", 16 | srcs = [ 17 | "issue.go", 18 | "issue_test.go", 19 | ], 20 | deps = [ 21 | "//api/proto:v1", 22 | "//pkg/enrichment/db", 23 | "//third_party/go:protobuf", 24 | "//third_party/go:stretchr_testify", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /pkg/enrichment/db/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "db", 3 | srcs = [ 4 | "db.go", 5 | "issue.go", 6 | ], 7 | resources = [":resources"], 8 | visibility = [ 9 | "//cmd/enricher", 10 | "//pkg/enrichment/...", 11 | "//producers/...", 12 | ], 13 | deps = [ 14 | "//api/proto:v1", 15 | "//third_party/go:golang-migrate_migrate", 16 | "//third_party/go:jmoiron_sqlx", 17 | "//third_party/go:lib_pq", 18 | "//third_party/go:protobuf", 19 | ], 20 | ) 21 | 22 | # This is a workaround for embedding files that are 23 | # not in the same pkg dir. 24 | filegroup( 25 | name = "resources", 26 | srcs = ["//enrichment_service/configs/sql/migrations"], 27 | ) 28 | -------------------------------------------------------------------------------- /pkg/enrichment/db/mock/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:go_mock") 2 | 3 | go_mock( 4 | name = "mock", 5 | interfaces = [ 6 | "EnrichDatabase", 7 | ], 8 | package = "github.com/thought-machine/dracon/pkg/enrichment/db", 9 | src_lib = "//pkg/enrichment/db", 10 | visibility = [ 11 | "//producers/...", 12 | ], 13 | deps = [ 14 | "//api/proto:v1", 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /pkg/enrichment/issue_test.go: -------------------------------------------------------------------------------- 1 | package enrichment 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | 8 | v1 "github.com/thought-machine/dracon/api/proto/v1" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestGetHash(t *testing.T) { 14 | expectedIssues := &v1.Issue{ 15 | Target: "pkg:golang/github.com/coreos/etcd@0.5.0-alpha.5", 16 | Type: "Vulnerable Dependency", 17 | Title: "[CVE-2018-1099] Improper Input Validation", 18 | Source: "git.foo.com/repo.git?ref=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 19 | Severity: v1.Severity_SEVERITY_MEDIUM, 20 | Cvss: 5.5, 21 | Confidence: v1.Confidence_CONFIDENCE_HIGH, 22 | Description: fmt.Sprintf("CVSS Score: %v\nCvssVector: %s\nCve: %s\nCwe: %s\nReference: %s\n", 23 | "5.5", "CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N", "CVE-2018-1099", 24 | "", "https://ossindex.sonatype.org/vuln/8a190129-526c-4ee0-b663-92f38139c165"), 25 | Cve: "123-321", 26 | 27 | } 28 | assert.Equal(t, GetHash(expectedIssues), "ccc217a4c2fd348bc5c6c4d73ad4311a") 29 | 30 | expectedIssues.Source = strings.NewReplacer("aa", "bc").Replace(expectedIssues.Source) 31 | // Test for regression on Bug where we would calculate ?ref=<> value for enrichment 32 | assert.Equal(t, GetHash(expectedIssues), "ccc217a4c2fd348bc5c6c4d73ad4311a") 33 | 34 | expectedIssues.Source = strings.NewReplacer("git.foo.com/repo.git", "https://example.com/foo/bar").Replace(expectedIssues.Source) 35 | assert.NotEqual(t, GetHash(expectedIssues), "3c73dcc2f7c647a4ff460249074a8d50") 36 | } 37 | -------------------------------------------------------------------------------- /pkg/kubernetes/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "kubernetes", 3 | srcs = [ 4 | "apply.go", 5 | ], 6 | visibility = ["PUBLIC"], 7 | deps = [ 8 | ], 9 | ) 10 | 11 | go_test( 12 | name = "kubernetes_test", 13 | srcs = [ 14 | "apply_test.go", 15 | ], 16 | deps = [ 17 | ":kubernetes", 18 | "//third_party/go:stretchr_testify", 19 | ], 20 | ) 21 | -------------------------------------------------------------------------------- /pkg/kubernetes/apply.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "os/exec" 8 | ) 9 | 10 | // KubectlOpts represents options/flags to pass to kubectl 11 | type KubectlOpts struct { 12 | Context string 13 | Namespace string 14 | } 15 | 16 | // Apply config using kubectl 17 | func Apply(resources string, opts *KubectlOpts) error { 18 | shCmd := GetCmd(opts) 19 | cmd := exec.Command(shCmd[0], shCmd[1:]...) 20 | stdin, err := cmd.StdinPipe() 21 | if err != nil { 22 | return fmt.Errorf("could not create stdin pipe: %w", err) 23 | } 24 | go func() { 25 | defer stdin.Close() 26 | _, err := io.WriteString(stdin, resources) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | }() 31 | 32 | output, err := cmd.CombinedOutput() 33 | if err != nil || !cmd.ProcessState.Success() { 34 | return fmt.Errorf("%s\n%s:%w", resources, output, err) 35 | } 36 | log.Printf("%s\n", output) 37 | return nil 38 | } 39 | 40 | // GetCmd returns the kubectl command 41 | func GetCmd(opts *KubectlOpts) []string { 42 | cmd := []string{"kubectl", "apply", "-f", "-"} 43 | 44 | if opts.Context != "" { 45 | cmd = append(cmd, fmt.Sprintf(`--context=%s`, opts.Context)) 46 | } 47 | if opts.Namespace != "" { 48 | cmd = append(cmd, fmt.Sprintf(`--namespace=%s`, opts.Namespace)) 49 | } 50 | 51 | return cmd 52 | } 53 | -------------------------------------------------------------------------------- /pkg/kubernetes/apply_test.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestGetCmd(t *testing.T) { 11 | var tests = []struct { 12 | desc string 13 | inOpts KubectlOpts 14 | outCmd string 15 | }{ 16 | {"none", KubectlOpts{}, "kubectl apply -f -"}, 17 | {"namespace", KubectlOpts{Namespace: "default"}, `kubectl apply -f - --namespace=default`}, 18 | {"context", KubectlOpts{Context: "minikube"}, `kubectl apply -f - --context=minikube`}, 19 | {"namespace&context", KubectlOpts{Context: "minikube", Namespace: "default"}, `kubectl apply -f - --context=minikube --namespace=default`}, 20 | } 21 | for _, tt := range tests { 22 | t.Run(tt.desc, func(t *testing.T) { 23 | outCmd := strings.Join(GetCmd(&tt.inOpts), " ") 24 | assert.Equal(t, tt.outCmd, outCmd) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pkg/putil/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "putil", 3 | srcs = [ 4 | "load.go", 5 | "write.go", 6 | ], 7 | visibility = ["PUBLIC"], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//third_party/go:gogo_protobuf", 11 | "//third_party/go:protobuf", 12 | ], 13 | ) 14 | -------------------------------------------------------------------------------- /pkg/putil/load.go: -------------------------------------------------------------------------------- 1 | package putil 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | 11 | v1 "github.com/thought-machine/dracon/api/proto/v1" 12 | 13 | "github.com/gogo/protobuf/proto" 14 | ) 15 | 16 | // LoadToolResponse loads raw results 17 | func LoadToolResponse(inPath string) ([]*v1.LaunchToolResponse, error) { 18 | responses := []*v1.LaunchToolResponse{} 19 | if err := filepath.Walk(inPath, func(path string, f os.FileInfo, err error) error { 20 | if f == nil { 21 | return fmt.Errorf("Path %s doesn't exist", path) 22 | } 23 | if !f.IsDir() && (strings.HasSuffix(f.Name(), ".pb")) { 24 | pbBytes, err := ioutil.ReadFile(path) 25 | if err != nil { 26 | return err 27 | } 28 | res := v1.LaunchToolResponse{} 29 | if err := proto.Unmarshal(pbBytes, &res); err != nil { 30 | log.Printf("skipping %s as unable to unmarshal", path) 31 | } else { 32 | responses = append(responses, &res) 33 | } 34 | } 35 | return nil 36 | }); err != nil { 37 | return responses, err 38 | } 39 | return responses, nil 40 | } 41 | 42 | // LoadEnrichedToolResponse loads enriched results from the enricher 43 | func LoadEnrichedToolResponse(inPath string) ([]*v1.EnrichedLaunchToolResponse, error) { 44 | responses := []*v1.EnrichedLaunchToolResponse{} 45 | if err := filepath.Walk(inPath, func(path string, f os.FileInfo, err error) error { 46 | if f == nil { 47 | return fmt.Errorf("Path %s doesn't exist", path) 48 | } 49 | if !f.IsDir() && (strings.HasSuffix(f.Name(), ".pb")) { 50 | pbBytes, err := ioutil.ReadFile(path) 51 | if err != nil { 52 | return err 53 | } 54 | res := v1.EnrichedLaunchToolResponse{} 55 | if err := proto.Unmarshal(pbBytes, &res); err != nil { 56 | log.Printf("skipping %s as unable to unmarshal", path) 57 | } else { 58 | responses = append(responses, &res) 59 | } 60 | } 61 | return nil 62 | }); err != nil { 63 | return responses, err 64 | } 65 | return responses, nil 66 | } 67 | -------------------------------------------------------------------------------- /pkg/template/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "template", 3 | srcs = [ 4 | "array-patch.go", 5 | "load.go", 6 | "patch.go", 7 | "pipeline.go", 8 | "pipeline-resource.go", 9 | "template.go", 10 | ], 11 | resources = [":resources"], 12 | visibility = ["PUBLIC"], 13 | deps = [ 14 | "//third_party/go:apimachinery", 15 | "//third_party/go:evanphx_json-patch", 16 | "//third_party/go:ghodss_yaml", 17 | "//third_party/go:speps_go-hashids", 18 | "//third_party/go:yaml_v3", 19 | ], 20 | ) 21 | 22 | go_test( 23 | name = "template_test", 24 | srcs = [ 25 | "load_test.go", 26 | ], 27 | resources = [":resources"], 28 | deps = [ 29 | ":template", 30 | "//third_party/go:heredoc", 31 | "//third_party/go:stretchr_testify", 32 | ], 33 | ) 34 | 35 | # This is a workaround for embedding files that are 36 | # not in the same pkg dir. 37 | filegroup( 38 | name = "resources", 39 | srcs = ["//resources/patches"], 40 | ) 41 | -------------------------------------------------------------------------------- /pkg/template/array-patch.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "strings" 8 | 9 | jsonpatch "github.com/evanphx/json-patch" 10 | ) 11 | 12 | func patchArrayGlob(op jsonpatch.Operation, targetJSON []byte) (jsonpatch.Patch, error) { 13 | path, err := op.Path() 14 | if err != nil || !strings.Contains(path, `/*/`) { 15 | return jsonpatch.Patch{op}, nil 16 | } 17 | return resolveArrayGlobOps(op, targetJSON) 18 | } 19 | 20 | func resolveArrayGlobOps(op jsonpatch.Operation, targetJSON []byte) (jsonpatch.Patch, error) { 21 | resOps := jsonpatch.Patch{} 22 | path, err := op.Path() 23 | if err != nil { 24 | return nil, fmt.Errorf("could not determine operation path: %w", err) 25 | } 26 | 27 | getLengthOfPath := func() (int, error) { 28 | var objMap map[string]*json.RawMessage 29 | err := json.Unmarshal(targetJSON, &objMap) 30 | if err != nil { 31 | return -1, fmt.Errorf("could parse target JSON: %w", err) 32 | } 33 | 34 | pathParts := strings.Split(path, "/") 35 | pathParts = pathParts[1:] 36 | for i, key := range pathParts { 37 | if rawJSON, ok := objMap[key]; ok { 38 | if pathParts[i+1] == "*" { 39 | var objArr []*json.RawMessage 40 | json.Unmarshal(*rawJSON, &objArr) 41 | return len(objArr), nil 42 | } 43 | err := json.Unmarshal(*rawJSON, &objMap) 44 | if err != nil { 45 | return -1, fmt.Errorf("could parse target JSON: %w", err) 46 | } 47 | } 48 | } 49 | return 0, nil 50 | } 51 | 52 | resolvedLength, err := getLengthOfPath() 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | for i := 0; i < resolvedLength; i++ { 58 | newOp := copyOperation(op) 59 | newPath := json.RawMessage(bytes.Replace(*newOp["path"], []byte(`/*/`), []byte(fmt.Sprintf("/%d/", i)), 1)) 60 | newOp["path"] = &newPath 61 | resOps = append(resOps, newOp) 62 | } 63 | 64 | return resOps, nil 65 | } 66 | -------------------------------------------------------------------------------- /pkg/template/array-patch_test.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | jsonpatch "github.com/evanphx/json-patch" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestArrayPatch(t *testing.T) { 12 | startPatchBytes := []byte(` 13 | [ 14 | { 15 | "op": "add", 16 | "path": "/spec/steps/*/volumeMounts/-", 17 | "value": { 18 | "mountPath": "/dracon", 19 | "name": "dracon-ws" 20 | } 21 | } 22 | ] 23 | `) 24 | startPatch, err := jsonpatch.DecodePatch(startPatchBytes) 25 | assert.Nil(t, err) 26 | 27 | targetJSON := []byte(` 28 | { 29 | "spec": { 30 | "steps": [ 31 | { 32 | "volumeMounts": [] 33 | }, 34 | { 35 | "volumeMounts": [] 36 | } 37 | ] 38 | } 39 | }`) 40 | 41 | resPatch, err := patchArrayGlob(startPatch[0], targetJSON) 42 | assert.Nil(t, err) 43 | 44 | resPatchJSON, err := json.Marshal(resPatch) 45 | assert.Nil(t, err) 46 | 47 | expectedPatchJSON := []byte(` 48 | [ 49 | { 50 | "op": "add", 51 | "path":"/spec/steps/0/volumeMounts/-", 52 | "value": { 53 | "mountPath": "/dracon", 54 | "name": "dracon-ws" 55 | } 56 | }, 57 | { 58 | "op": "add", 59 | "path":"/spec/steps/1/volumeMounts/-", 60 | "value": { 61 | "mountPath": "/dracon", 62 | "name": "dracon-ws" 63 | } 64 | } 65 | ] 66 | `) 67 | 68 | assert.JSONEq(t, string(expectedPatchJSON), string(resPatchJSON)) 69 | } 70 | -------------------------------------------------------------------------------- /pkg/template/pipeline.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | // non-runtime 4 | import ( 5 | "encoding/json" 6 | "strings" 7 | ) 8 | 9 | // PipelineParam represents a Parameter in a Pipeline 10 | type PipelineParam struct { 11 | Name string 12 | Description string 13 | Type string 14 | 15 | Value string 16 | } 17 | 18 | // PipelineTask represents a Task in a Pipeline 19 | type PipelineTask struct { 20 | Index int 21 | Name string 22 | } 23 | 24 | func preparePipelineVars(doc []byte) error { 25 | var err error 26 | TemplateVars.PipelineTaskProducers, err = getPipelineTasksByType("producer", doc) 27 | if err != nil { 28 | return err 29 | } 30 | TemplateVars.PipelineTaskConsumers, err = getPipelineTasksByType("consumer", doc) 31 | if err != nil { 32 | return err 33 | } 34 | TemplateVars.PipelineTaskEnrichers, err = getPipelineTasksByType("enricher", doc) 35 | if err != nil { 36 | return err 37 | } 38 | return nil 39 | } 40 | 41 | func getPipelineTasksByType(taskType string, targetJSON []byte) ([]PipelineTask, error) { 42 | var p pipeline 43 | err := json.Unmarshal(targetJSON, &p) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | pipelineTasks := []PipelineTask{} 49 | for i, t := range p.Spec.Tasks { 50 | tTaskType := t.Name 51 | taskName := t.Name 52 | nameParts := strings.Split(t.Name, "-") 53 | if len(nameParts) > 1 { 54 | tTaskType = nameParts[len(nameParts)-1] 55 | taskName = strings.Join(nameParts[0:len(nameParts)-1], "-") 56 | } 57 | if tTaskType == taskType { 58 | pipelineTasks = append(pipelineTasks, PipelineTask{Index: i, Name: taskName}) 59 | } 60 | } 61 | 62 | return pipelineTasks, nil 63 | } 64 | 65 | type pipeline struct { 66 | Spec struct { 67 | Tasks []struct { 68 | Name string `json:"name"` 69 | } `json:"tasks"` 70 | } `json:"spec"` 71 | } 72 | -------------------------------------------------------------------------------- /pkg/version/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "version", 3 | srcs = [ 4 | "version.go", 5 | ], 6 | visibility = ["PUBLIC"], 7 | deps = [ 8 | ], 9 | ) 10 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | // BuildVersion is the version of dracon. It's intended to be overriden using 4 | // -X the linker flag. 5 | var BuildVersion = "dev" 6 | 7 | // TODO(hjenkins): Implement fetching the k8s and tekton version from the server 8 | -------------------------------------------------------------------------------- /producers/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "producers", 3 | srcs = [ 4 | "producer.go", 5 | ], 6 | visibility = ["//producers/..."], 7 | deps = [ 8 | "//api/proto:v1", 9 | "//pkg/putil", 10 | "//third_party/go:protobuf", 11 | ], 12 | ) 13 | 14 | go_test( 15 | name = "producers_go_test", 16 | srcs = [ 17 | "producer.go", 18 | "producer_test.go", 19 | ], 20 | deps = [ 21 | "//api/proto:v1", 22 | "//pkg/putil", 23 | "//third_party/go:gogo_protobuf", 24 | "//third_party/go:protobuf", 25 | "//third_party/go:stretchr_testify", 26 | ], 27 | ) 28 | 29 | # TODO(hjenkins) support building python 30 | -------------------------------------------------------------------------------- /producers/README.md: -------------------------------------------------------------------------------- 1 | ## Producers 2 | 3 | A producer is a program that parses the output of a tool and converts it into Dracon compatible file that can be used by the *enricher* and *consumers*. 4 | 5 | ### Writing Producers 6 | 7 | Producers can be written in any language that supports protobufs, we have examples in Golang and Python. They are all structured the same way: 8 | 1. Parse program arguments: 9 | 1. `in`: the raw tool results file location 10 | 2. `out`: where to place the Dracon compatible output file location 11 | 2. Parse the `in` file into Protobufs (`LaunchToolResponse`) 12 | 3. Add metadata to Protobufs (e.g. git/source-code information) 13 | 4. Write the protobuf bytes to the `out` file 14 | 15 | ### Producer API 16 | For convenience, there are helper functions in the `./producers` pkg/module for Golang/Python. 17 | 18 | The `WriteDraconOut`/`write_dracon_out` method expects a list of issues to write as the `LaunchToolResponse` protobuf. Your producer should parse the output of a tool results into `Issue` protobufs which are then passed into this method. 19 | -------------------------------------------------------------------------------- /producers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thought-machine/dracon/afa59ace9a311a5ab1f171450b720b7ac054365a/producers/__init__.py -------------------------------------------------------------------------------- /producers/dependency_check/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "dependency_check", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | ], 12 | ) 13 | 14 | go_test( 15 | name = "dependency_check_test", 16 | srcs = [ 17 | "main.go", 18 | "main_test.go", 19 | ], 20 | deps = [ 21 | "//api/proto:v1", 22 | "//producers", 23 | "//third_party/go:stretchr_testify", 24 | ], 25 | ) 26 | 27 | docker_image( 28 | name = "dracon-producer-dependency-check", 29 | srcs = [ 30 | ":dependency_check", 31 | ], 32 | base_image = "//build/docker:dracon-base-go", 33 | image = "dracon-producer-dependency-check", 34 | ) 35 | -------------------------------------------------------------------------------- /producers/dependency_check/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY dependency_check /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/docker_trivy/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | # this producer covers trivy https://github.com/aquasecurity/trivy 4 | 5 | go_binary( 6 | name = "trivy", 7 | srcs = [ 8 | "main.go", 9 | ], 10 | deps = [ 11 | "//api/proto:v1", 12 | "//producers", 13 | "//producers/docker_trivy/types", 14 | ], 15 | ) 16 | 17 | go_test( 18 | name = "trivy_test", 19 | srcs = [ 20 | "main.go", 21 | "main_test.go", 22 | ], 23 | deps = [ 24 | "//api/proto:v1", 25 | "//producers", 26 | "//producers/docker_trivy/types", 27 | "//third_party/go:stretchr_testify", 28 | ], 29 | ) 30 | 31 | docker_image( 32 | name = "dracon-producer-trivy", 33 | srcs = [ 34 | ":trivy", 35 | ], 36 | base_image = "//build/docker:dracon-base-go", 37 | image = "dracon-producer-trivy", 38 | ) 39 | -------------------------------------------------------------------------------- /producers/docker_trivy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY trivy /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/docker_trivy/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "trivy-issue.go", 5 | ], 6 | visibility = ["//producers/docker_trivy/..."], 7 | ) 8 | -------------------------------------------------------------------------------- /producers/docker_trivy/types/trivy-issue.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // CombinedOut represents the output of multiple Trivy runs (useful when using the Trivy Dracon tool to scan multiple images); the key is the name of the image file that was scanned 4 | type CombinedOut map[string][]TrivyOut 5 | 6 | // TrivyOut represents the output of a trivy run that we care about 7 | type TrivyOut struct { 8 | Vulnerable []TrivyVulnerability `json:"Vulnerabilities"` 9 | Target string 10 | Type string 11 | } 12 | 13 | // TrivyVulnerability represents a trivy vulnerability section with only the fields that we care about 14 | type TrivyVulnerability struct { 15 | CVE string `json:"VulnerabilityID"` 16 | PkgName string `json:"PkgName"` 17 | InstalledVersion string `json:"InstalledVersion"` 18 | FixedVersion string `json:"FixedVersion"` 19 | PrimaryURL string `json:"PrimaryURL"` 20 | Title string `json:"Title"` 21 | Description string `json:"Description"` 22 | Severity string `json:"Severity"` 23 | CweIDs []string `json:"CweIds"` 24 | CVSS TrivyCVSS `json:"CVSS"` 25 | } 26 | 27 | // TrivyCVSS wraps Trivy CVSS info struct 28 | type TrivyCVSS struct { 29 | Nvd TrivyNvd `json:"nvd"` 30 | } 31 | 32 | // TrivyNvd wraps Trivy Nvd structure inside the CVSS struct 33 | type TrivyNvd struct { 34 | V3Vector string `json:"V3Vector"` 35 | V3Score float64 `json:"V3Score"` 36 | } 37 | -------------------------------------------------------------------------------- /producers/elasticsearch_filebeat/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "elasticsearch_filebeat", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | "//producers/elasticsearch_filebeat/types", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "elasticsearch_filebeat_test", 17 | srcs = [ 18 | "main.go", 19 | "main_test.go", 20 | ], 21 | deps = [ 22 | "//api/proto:v1", 23 | "//producers", 24 | "//producers/elasticsearch_filebeat/types", 25 | "//third_party/go:stretchr_testify", 26 | ], 27 | ) 28 | 29 | docker_image( 30 | name = "dracon-producer-elasticsearchfilebeat", 31 | srcs = [ 32 | ":elasticsearch_filebeat", 33 | ], 34 | base_image = "//build/docker:dracon-base-go", 35 | image = "dracon-producer-elasticsearch-filebeat", 36 | ) 37 | -------------------------------------------------------------------------------- /producers/elasticsearch_filebeat/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY elasticsearch_filebeat /elasticsearch_filebeat 4 | 5 | ENTRYPOINT ["/elasticsearch_filebeat"] 6 | -------------------------------------------------------------------------------- /producers/elasticsearch_filebeat/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | v1 "github.com/thought-machine/dracon/api/proto/v1" 8 | "github.com/thought-machine/dracon/producers/elasticsearch_filebeat/types" 9 | 10 | "github.com/thought-machine/dracon/producers" 11 | ) 12 | 13 | func main() { 14 | if err := producers.ParseFlags(); err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | inFile, err := producers.ReadInFile() 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | var results types.ElasticSearchFilebeatResult 24 | if err := producers.ParseJSON(inFile, &results); err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | issues := parseIssues(&results) 29 | if err := producers.WriteDraconOut( 30 | "elasticsearch-filebeat", 31 | issues, 32 | ); err != nil { 33 | log.Fatal(err) 34 | } 35 | } 36 | 37 | func parseIssues(results *types.ElasticSearchFilebeatResult) []*v1.Issue { 38 | issues := []*v1.Issue{} 39 | 40 | for _, h := range results.Hits.Hits { 41 | 42 | issues = append(issues, &v1.Issue{ 43 | Target: fmt.Sprintf("%s", h.Source.Host.Name), 44 | Type: "Antivirus Issue", 45 | Title: fmt.Sprintf("Antivirus Issue on %s", h.Source.Host.Name), 46 | Severity: v1.Severity_SEVERITY_INFO, 47 | Cvss: 0.0, 48 | Confidence: v1.Confidence_CONFIDENCE_MEDIUM, 49 | Description: h.Source.Message, 50 | }) 51 | 52 | } 53 | 54 | for _, b := range results.Aggregations.Aggregation.Buckets { 55 | 56 | name := b.Name 57 | h := b.Metric.Hits.Hits[0] 58 | issues = append(issues, &v1.Issue{ 59 | Target: fmt.Sprintf("%s", name), 60 | Type: "Antivirus Issue", 61 | Title: fmt.Sprintf("Antivirus Issue on %s", name), 62 | Severity: v1.Severity_SEVERITY_INFO, 63 | Cvss: 0.0, 64 | Confidence: v1.Confidence_CONFIDENCE_MEDIUM, 65 | Description: h.Source.Message, 66 | }) 67 | 68 | } 69 | 70 | return issues 71 | } 72 | -------------------------------------------------------------------------------- /producers/elasticsearch_filebeat/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "elasticsearch-filebeat-issue.go", 5 | ], 6 | visibility = ["//producers/elasticsearch_filebeat/..."], 7 | ) 8 | -------------------------------------------------------------------------------- /producers/elasticsearch_filebeat/types/elasticsearch-filebeat-issue.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // ElasticSearchFilebeatResult represents how a filebeat result appears in elasticsearch 4 | type ElasticSearchFilebeatResult struct { 5 | Hits elasticSearchFilebeatHits `json:"hits"` 6 | Aggregations elasticSearchFilebeatAggregations `json:"aggregations"` 7 | } 8 | 9 | type elasticSearchFilebeatHits struct { 10 | Hits []elasticSearchFilebeatHit `json:"hits"` 11 | } 12 | 13 | type elasticSearchFilebeatHit struct { 14 | ID string `json:"_id"` 15 | Source elasticSearchFilebeatSource `json:"_source"` 16 | } 17 | 18 | type elasticSearchFilebeatSource struct { 19 | Message string `json:"message"` 20 | Timestamp string `json:"@timestamp"` 21 | Host elasticSearchFilebeatHost `json:"host"` 22 | } 23 | 24 | type elasticSearchFilebeatHost struct { 25 | Name string `json:"name"` 26 | } 27 | 28 | // Note that the aggregation field name depends on the request. 29 | type elasticSearchFilebeatAggregations struct { 30 | Aggregation elasticSearchFilebeatAggregation `json:"aggregation"` 31 | } 32 | 33 | type elasticSearchFilebeatAggregation struct { 34 | Buckets []elasticSearchFilebeatBucket `json:"buckets"` 35 | } 36 | 37 | // Note that the metric field name depends on the request. 38 | type elasticSearchFilebeatBucket struct { 39 | Name string `json:"key"` 40 | Metric elasticSearchFilebeatMetric `json:"metric"` 41 | } 42 | 43 | type elasticSearchFilebeatMetric struct { 44 | Hits elasticSearchFilebeatHits `json:"hits"` 45 | } 46 | -------------------------------------------------------------------------------- /producers/examples_test.go: -------------------------------------------------------------------------------- 1 | package producers 2 | 3 | import ( 4 | "log" 5 | 6 | v1 "github.com/thought-machine/dracon/api/proto/v1" 7 | ) 8 | 9 | func ExampleParseFlags() { 10 | if err := ParseFlags(); err != nil { 11 | log.Fatal(err) 12 | } 13 | } 14 | 15 | func ExampleParseInFileJSON() { 16 | type GoSecOut struct { 17 | Issues []struct { 18 | Severity string `json:"severity"` 19 | Confidence string `json:"confidence"` 20 | RuleID string `json:"rule_id"` 21 | Details string `json:"details"` 22 | File string `json:"file"` 23 | Code string `json:"code"` 24 | Line string `json:"line"` 25 | Column string `json:"column"` 26 | } `json:"Issues"` 27 | } 28 | var results GoSecOut 29 | if err := ParseInFileJSON(&results); err != nil { 30 | log.Fatal(err) 31 | } 32 | } 33 | 34 | func ExampleWriteDraconOut() { 35 | issues := []*v1.Issue{} 36 | if err := WriteDraconOut( 37 | "gosec", 38 | issues, 39 | ); err != nil { 40 | log.Fatal(err) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /producers/golang_gosec/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "golang_gosec", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | ], 12 | ) 13 | 14 | go_test( 15 | name = "golang_gosec_test", 16 | srcs = [ 17 | "main.go", 18 | "main_test.go", 19 | ], 20 | deps = [ 21 | "//api/proto:v1", 22 | "//producers", 23 | "//third_party/go:stretchr_testify", 24 | ], 25 | ) 26 | 27 | docker_image( 28 | name = "dracon-producer-gosec", 29 | srcs = [ 30 | ":golang_gosec", 31 | ], 32 | base_image = "//build/docker:dracon-base-go", 33 | image = "dracon-producer-gosec", 34 | visibility = ["//examples/..."], 35 | ) 36 | -------------------------------------------------------------------------------- /producers/golang_gosec/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY golang_gosec /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/golang_gosec/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | v1 "github.com/thought-machine/dracon/api/proto/v1" 8 | 9 | "github.com/thought-machine/dracon/producers" 10 | ) 11 | 12 | func main() { 13 | if err := producers.ParseFlags(); err != nil { 14 | log.Fatal(err) 15 | } 16 | 17 | inFile, err := producers.ReadInFile() 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | var results GoSecOut 23 | if err := producers.ParseJSON(inFile, &results); err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | issues := parseIssues(&results) 28 | 29 | if err := producers.WriteDraconOut( 30 | "gosec", 31 | issues, 32 | ); err != nil { 33 | log.Fatal(err) 34 | } 35 | } 36 | 37 | func parseIssues(out *GoSecOut) []*v1.Issue { 38 | issues := []*v1.Issue{} 39 | for _, r := range out.Issues { 40 | issues = append(issues, &v1.Issue{ 41 | Target: fmt.Sprintf("%s:%v", r.File, r.Line), 42 | Type: r.RuleID, 43 | Title: r.Details, 44 | Severity: v1.Severity(v1.Severity_value[fmt.Sprintf("SEVERITY_%s", r.Severity)]), 45 | Cvss: 0.0, 46 | Confidence: v1.Confidence(v1.Confidence_value[fmt.Sprintf("CONFIDENCE_%s", r.Confidence)]), 47 | Description: r.Code, 48 | }) 49 | } 50 | return issues 51 | } 52 | 53 | // GoSecOut represents the output of a GoSec run 54 | type GoSecOut struct { 55 | Issues []GoSecIssue `json:"Issues"` 56 | // Stats GoSecStats `json:"Stats"` 57 | } 58 | 59 | // GoSecIssue represents a GoSec Result 60 | type GoSecIssue struct { 61 | Severity string `json:"severity"` 62 | Confidence string `json:"confidence"` 63 | RuleID string `json:"rule_id"` 64 | Details string `json:"details"` 65 | File string `json:"file"` 66 | Code string `json:"code"` 67 | Line string `json:"line"` 68 | Column string `json:"column"` 69 | } 70 | -------------------------------------------------------------------------------- /producers/golang_gosec/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | v1 "github.com/thought-machine/dracon/api/proto/v1" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | const exampleOutput = ` 13 | { 14 | "Issues": [ 15 | { 16 | "severity": "MEDIUM", 17 | "confidence": "HIGH", 18 | "rule_id": "G304", 19 | "details": "Potential file inclusion via variable", 20 | "file": "/tmp/source/foo.go", 21 | "code": "ioutil.ReadFile(path)", 22 | "line": "33", 23 | "column": "44" 24 | } 25 | ], 26 | "Stats": { 27 | "files": 1, 28 | "lines": 60, 29 | "nosec": 0, 30 | "found": 1 31 | } 32 | }` 33 | 34 | func TestParseIssues(t *testing.T) { 35 | var results GoSecOut 36 | err := json.Unmarshal([]byte(exampleOutput), &results) 37 | assert.Nil(t, err) 38 | 39 | issues := parseIssues(&results) 40 | 41 | expectedIssue := &v1.Issue{ 42 | Target: "/tmp/source/foo.go:33", 43 | Type: "G304", 44 | Title: "Potential file inclusion via variable", 45 | Severity: v1.Severity_SEVERITY_MEDIUM, 46 | Cvss: 0.0, 47 | Confidence: v1.Confidence_CONFIDENCE_HIGH, 48 | Description: "ioutil.ReadFile(path)", 49 | } 50 | 51 | assert.Equal(t, []*v1.Issue{expectedIssue}, issues) 52 | } 53 | -------------------------------------------------------------------------------- /producers/golang_nancy/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | # this producer covers nancy https://github.com/sonatype-nexus-community/nancy 4 | 5 | go_binary( 6 | name = "nancy", 7 | srcs = [ 8 | "main.go", 9 | ], 10 | deps = [ 11 | "//api/proto:v1", 12 | "//producers", 13 | "//producers/golang_nancy/types", 14 | ], 15 | ) 16 | 17 | go_test( 18 | name = "nancy_test", 19 | srcs = [ 20 | "main.go", 21 | "main_test.go", 22 | ], 23 | deps = [ 24 | "//api/proto:v1", 25 | "//producers", 26 | "//producers/golang_nancy/types", 27 | "//third_party/go:stretchr_testify", 28 | ], 29 | ) 30 | 31 | docker_image( 32 | name = "dracon-producer-nancy", 33 | srcs = [ 34 | ":nancy", 35 | ], 36 | base_image = "//build/docker:dracon-base-go", 37 | image = "dracon-producer-nancy", 38 | ) 39 | -------------------------------------------------------------------------------- /producers/golang_nancy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY nancy /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/golang_nancy/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "nancy-issue.go", 5 | ], 6 | visibility = ["//producers/golang_nancy/..."], 7 | ) 8 | -------------------------------------------------------------------------------- /producers/golang_nancy/types/nancy-issue.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // NancyOut represents the output of a nancy run that we care about 4 | type NancyOut struct { 5 | Vulnerable []NancyAdvisories `json:"vulnerable"` 6 | Audited []interface{} 7 | Exclusions []interface{} 8 | Invalid []interface{} 9 | NumAudited int 10 | NumVulnerable int 11 | Version string 12 | } 13 | 14 | // NancyAdvisories represents a nancy advisory section that we care about 15 | type NancyAdvisories struct { 16 | Coordinates string `json:"Coordinates"` 17 | Reference string `json:"Reference"` 18 | Vulnerabilities []NancyVulnerabilities `json:"Vulnerabilities"` 19 | } 20 | 21 | // NancyVulnerabilities represents a nancy vulnerability 22 | type NancyVulnerabilities struct { 23 | ID string `json:"Id"` 24 | Title string `json:"Title"` 25 | Description string `json:"Description"` 26 | CvssScore string `json:"CvssScore"` 27 | CvssVector string `json:"CvssVector"` 28 | Cve string `json:"Cve"` 29 | Cwe string `json:"Cwe"` 30 | Reference string `json:"Reference"` 31 | } 32 | -------------------------------------------------------------------------------- /producers/java_spotbugs/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "java_spotbugs", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | ], 12 | ) 13 | 14 | go_test( 15 | name = "java_spotbugs_test", 16 | srcs = [ 17 | "main.go", 18 | "main_test.go", 19 | ], 20 | deps = [ 21 | "//api/proto:v1", 22 | "//producers", 23 | "//third_party/go:protobuf", 24 | "//third_party/go:stretchr_testify", 25 | ], 26 | ) 27 | 28 | docker_image( 29 | name = "dracon-producer-java_spotbugs", 30 | srcs = [ 31 | ":java_spotbugs", 32 | ], 33 | base_image = "//build/docker:dracon-base-go", 34 | image = "dracon-producer-java_spotbugs", 35 | visibility = ["//examples/..."], 36 | ) 37 | -------------------------------------------------------------------------------- /producers/java_spotbugs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY java_spotbugs /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/jira_producer/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "sync_tickets", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//common/jira/config", 10 | "//common/jira/jira", 11 | "//pkg/enrichment/db", 12 | "//producers/jira_producer/sync", 13 | "//third_party/go:go-jira", 14 | "//third_party/go:spf13_cobra", 15 | "//third_party/go:spf13_viper", 16 | ], 17 | ) 18 | 19 | docker_image( 20 | name = "dracon-producer-jira", 21 | srcs = [ 22 | ":sync_tickets", 23 | ], 24 | base_image = "//build/docker:dracon-base-go", 25 | image = "dracon-producer-jira", 26 | ) 27 | -------------------------------------------------------------------------------- /producers/jira_producer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY sync_tickets /sync 4 | 5 | ENTRYPOINT ["/sync"] 6 | -------------------------------------------------------------------------------- /producers/jira_producer/README.md: -------------------------------------------------------------------------------- 1 | Jira Synchronizer 2 | === 3 | This "Producer" for lack of a better term will read from the Jira instance it gets pointed to and synchronise triaged vulnerabilities with the internal Dracon DB, this allows Dracon to understand when a vulnerability has been resolved so it can remove it from the list of duplicates, it also allows for marking vulnerabilities as false positives. 4 | 5 | A cronjob has been created to make this synchronisation periodic. You can find a template for it under //examples/cronjobs/jira-sync-cronjob.yaml 6 | This component utilises the default Jira config.yaml that the Jira consumer uses. 7 | 8 | To run this individually: 9 | ``` plz run //producers/jira_producer:sync_tickets -- --user="" --token="" --jira="<>" --query='' --config /path/to/config.yaml --dbcon "" 10 | ``` 11 | -------------------------------------------------------------------------------- /producers/jira_producer/sync/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "sync", 3 | srcs = [ 4 | "sync.go", 5 | ], 6 | visibility = ["//producers/..."], 7 | deps = [ 8 | "//api/proto:v1", 9 | "//common/jira/config", 10 | "//common/jira/document", 11 | "//pkg/enrichment", 12 | "//pkg/enrichment/db", 13 | "//third_party/go:go-jira", 14 | "//third_party/go:mitchellh_mapstructure", 15 | "//third_party/go:tgo", 16 | ], 17 | ) 18 | 19 | go_test( 20 | name = "sync_test", 21 | srcs = [ 22 | "sync.go", 23 | "sync_test.go", 24 | ], 25 | deps = [ 26 | "//api/proto:v1", 27 | "//common/jira/config", 28 | "//common/jira/document", 29 | "//pkg/enrichment", 30 | "//pkg/enrichment/db", 31 | "//pkg/enrichment/db/mock", 32 | "//third_party/go:go-jira", 33 | "//third_party/go:mitchellh_mapstructure", 34 | "//third_party/go:mock", 35 | "//third_party/go:stretchr_testify", 36 | "//third_party/go:tgo", 37 | ], 38 | ) 39 | -------------------------------------------------------------------------------- /producers/mobsf/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "entrypoint", 5 | srcs = [ 6 | "cli.go", 7 | "main.go", 8 | "project.go", 9 | ], 10 | out = "entrypoint", 11 | deps = [ 12 | "//api/proto:v1", 13 | "//pkg/template", 14 | "//producers", 15 | "//producers/mobsf/report", 16 | "//producers/mobsf/report/android", 17 | "//producers/mobsf/report/ios", 18 | ], 19 | ) 20 | 21 | docker_image( 22 | name = "mobsf", 23 | srcs = [ 24 | ":entrypoint", 25 | ], 26 | dockerfile = "Dockerfile-producer-mobsf", 27 | image = "dracon-producer-mobsf", 28 | ) 29 | -------------------------------------------------------------------------------- /producers/mobsf/Dockerfile-producer-mobsf: -------------------------------------------------------------------------------- 1 | FROM opensecurity/mobile-security-framework-mobsf:v3.1.1 as mobsf 2 | 3 | COPY /entrypoint / 4 | 5 | WORKDIR / 6 | ENTRYPOINT ["/entrypoint"] 7 | -------------------------------------------------------------------------------- /producers/mobsf/project.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/url" 5 | ) 6 | 7 | // ProjectType represents a particular type of mobile app project supported by 8 | // MobSF. 9 | type ProjectType string 10 | 11 | // MobSF constants 12 | const ( 13 | AndroidEclipse ProjectType = "Android Eclipse" 14 | AndroidStudio = "Android Studio" 15 | XcodeIos = "Xcode iOS" 16 | ) 17 | 18 | // Project represents a particular project somewhere in the target code base. 19 | type Project struct { 20 | RootDir string 21 | Type ProjectType 22 | Upload *MobSFFile 23 | } 24 | 25 | // MobSFFile represents a file stored in MobSF. This is typically a project code 26 | // base that has been uploaded to MobSF via the REST API or web interface. 27 | type MobSFFile struct { 28 | FileName string `json:"file_name"` 29 | Hash string `json:"hash"` 30 | ScanType string `json:"scan_type"` 31 | } 32 | 33 | // AsScanQuery returns a string representation of the MobSFFile that identifies 34 | // the corresponding server-side file as part of a request to MobSF's scan 35 | // endpoint. 36 | func (m *MobSFFile) AsScanQuery() string { 37 | v := url.Values{} 38 | 39 | v.Add("file_name", m.FileName) 40 | v.Add("hash", m.Hash) 41 | v.Add("scan_type", m.ScanType) 42 | 43 | return v.Encode() 44 | } 45 | 46 | // AsReportQuery returns a string representation of the MobSFFile that 47 | // identifies the corresponding server-side file as part of a request to any of 48 | // MobSF's report generation endpoints. 49 | func (m *MobSFFile) AsReportQuery() string { 50 | v := url.Values{} 51 | 52 | v.Add("hash", m.Hash) 53 | 54 | return v.Encode() 55 | } 56 | -------------------------------------------------------------------------------- /producers/mobsf/report/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "report", 3 | srcs = [ 4 | "report.go", 5 | ], 6 | visibility = ["//producers/mobsf/..."], 7 | deps = [ 8 | "//api/proto:v1", 9 | ], 10 | ) 11 | -------------------------------------------------------------------------------- /producers/mobsf/report/android/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "android", 3 | srcs = [ 4 | "android.go", 5 | ], 6 | visibility = ["//producers/mobsf/..."], 7 | deps = [ 8 | "//api/proto:v1", 9 | "//producers/mobsf/report", 10 | ], 11 | ) 12 | 13 | go_test( 14 | name = "android_test", 15 | srcs = [ 16 | "android_test.go", 17 | ], 18 | deps = [ 19 | ":android", 20 | "//api/proto:v1", 21 | "//producers", 22 | "//third_party/go:stretchr_testify", 23 | ], 24 | ) 25 | -------------------------------------------------------------------------------- /producers/mobsf/report/android/android.go: -------------------------------------------------------------------------------- 1 | // Package android provides types and functions for working with Android project 2 | // scan reports from MobSF. 3 | package android 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "path/filepath" 9 | "strings" 10 | 11 | "github.com/thought-machine/dracon/api/proto/v1" 12 | mreport "github.com/thought-machine/dracon/producers/mobsf/report" 13 | ) 14 | 15 | // Report represents a (partial) Android project scan report. 16 | type Report struct { 17 | RootDir string `json:"-"` 18 | CodeAnalysis map[string]mreport.CodeAnalysisFinding `json:"code_analysis"` 19 | CodeAnalysisExclusions map[string]bool `json:"-"` 20 | } 21 | 22 | // NewReport instantiates a report 23 | func NewReport(report []byte, exclusions map[string]bool) (mreport.Report, error) { 24 | var r *Report 25 | if err := json.Unmarshal(report, &r); err != nil { 26 | return nil, err 27 | } 28 | 29 | r.CodeAnalysisExclusions = exclusions 30 | 31 | return r, nil 32 | } 33 | 34 | // SetRootDir is a helper method 35 | func (r *Report) SetRootDir(path string) { 36 | r.RootDir = path 37 | } 38 | 39 | // AsIssues returns mobsf android findings as dracon issues 40 | func (r *Report) AsIssues() []*v1.Issue { 41 | issues := make([]*v1.Issue, 0) 42 | 43 | for id, finding := range r.CodeAnalysis { 44 | if _, exists := r.CodeAnalysisExclusions[id]; exists { 45 | continue 46 | } 47 | 48 | for filename, linesList := range finding.Files { 49 | for _, line := range strings.Split(linesList, ",") { 50 | issues = append(issues, &v1.Issue{ 51 | Target: fmt.Sprintf("%s:%s", filepath.Join(r.RootDir, filename), line), 52 | Type: id, 53 | Title: finding.Metadata.CWE, 54 | Severity: v1.Severity(v1.Severity_value[fmt.Sprintf("SEVERITY_%s", strings.ToUpper(finding.Metadata.Severity))]), 55 | Cvss: finding.Metadata.CVSS, 56 | Confidence: v1.Confidence_CONFIDENCE_INFO, 57 | Description: finding.Metadata.Description, 58 | }) 59 | } 60 | } 61 | } 62 | 63 | return issues 64 | } 65 | -------------------------------------------------------------------------------- /producers/mobsf/report/ios/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "ios", 3 | srcs = [ 4 | "ios.go", 5 | ], 6 | visibility = ["//producers/mobsf/..."], 7 | deps = [ 8 | "//api/proto:v1", 9 | "//producers/mobsf/report", 10 | ], 11 | ) 12 | 13 | go_test( 14 | name = "ios_test", 15 | srcs = [ 16 | "ios_test.go", 17 | ], 18 | deps = [ 19 | ":ios", 20 | "//api/proto:v1", 21 | "//producers", 22 | "//third_party/go:stretchr_testify", 23 | ], 24 | ) 25 | -------------------------------------------------------------------------------- /producers/mobsf/report/report.go: -------------------------------------------------------------------------------- 1 | // Package report provides common types for scan report formats. 2 | package report 3 | 4 | import ( 5 | v1 "github.com/thought-machine/dracon/api/proto/v1" 6 | ) 7 | 8 | // Report is an interface for scan report formats. 9 | type Report interface { 10 | // SetRootDir sets the path to this project's root directory. 11 | SetRootDir(string) 12 | 13 | // AsIssues transforms this Report into a slice of Dracon Issues that can be 14 | // processed by the Dracon enricher. 15 | AsIssues() []*v1.Issue 16 | } 17 | 18 | // CodeAnalysisFinding Describes a mobsf finding 19 | type CodeAnalysisFinding struct { 20 | Files map[string]string `json:"files"` 21 | Metadata codeAnalysisMetadata `json:"metadata"` 22 | } 23 | 24 | type codeAnalysisMetadata struct { 25 | CVSS float64 `json:"cvss"` 26 | CWE string `json:"cwe"` 27 | Description string `json:"description"` 28 | Severity string `json:"severity"` 29 | } 30 | -------------------------------------------------------------------------------- /producers/npm_audit/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "npm_audit", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | "//producers/npm_audit/types", 12 | "//producers/npm_audit/types/npmfullaudit", 13 | "//producers/npm_audit/types/npmquickaudit", 14 | ], 15 | ) 16 | 17 | docker_image( 18 | name = "dracon-producer-npmaudit", 19 | srcs = [ 20 | ":npm_audit", 21 | ], 22 | base_image = "//build/docker:dracon-base-go", 23 | image = "dracon-producer-npm-audit", 24 | ) 25 | -------------------------------------------------------------------------------- /producers/npm_audit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY npm_audit /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/npm_audit/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/thought-machine/dracon/producers" 5 | atypes "github.com/thought-machine/dracon/producers/npm_audit/types" 6 | "github.com/thought-machine/dracon/producers/npm_audit/types/npmfullaudit" 7 | "github.com/thought-machine/dracon/producers/npm_audit/types/npmquickaudit" 8 | 9 | "errors" 10 | "flag" 11 | "log" 12 | ) 13 | 14 | // producer flags 15 | var ( 16 | PackagePath string 17 | ) 18 | 19 | func inFileToReport(inFile []byte) (atypes.Report, error) { 20 | reportConstructors := []func([]byte) (atypes.Report, error){ 21 | npmquickaudit.NewReport, 22 | npmfullaudit.NewReport, 23 | } 24 | 25 | for _, constructor := range reportConstructors { 26 | report, err := constructor(inFile) 27 | 28 | switch err.(type) { 29 | case nil: 30 | return report, nil 31 | case *atypes.ParsingError, *atypes.FormatError: 32 | // Ignore parsing and incorrect format errors from constructors - 33 | // we'll just attempt again with the next one 34 | default: 35 | // Any other errors returned by a constructor are likely fatal 36 | return nil, err 37 | } 38 | } 39 | 40 | return nil, errors.New("input file is not a supported npm audit report format") 41 | } 42 | 43 | func main() { 44 | flag.StringVar(&PackagePath, "package-path", "", "Path to the package.json file corresponding to this audit report; will be prepended to vulnerable dependency names in issue reports if specified") 45 | 46 | if err := producers.ParseFlags(); err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | inFile, err := producers.ReadInFile() 51 | if err != nil { 52 | log.Fatal(err) 53 | } 54 | 55 | report, err := inFileToReport(inFile) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | 60 | log.Printf("Parsed input file as %s\n", report.Type()) 61 | 62 | report.SetPackagePath(PackagePath) 63 | 64 | if err := producers.WriteDraconOut( 65 | "npm-audit", 66 | report.AsIssues(), 67 | ); err != nil { 68 | log.Fatal(err) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /producers/npm_audit/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "errors.go", 5 | "types.go", 6 | ], 7 | visibility = ["//producers/npm_audit/..."], 8 | deps = [ 9 | "//api/proto:v1", 10 | ], 11 | ) 12 | -------------------------------------------------------------------------------- /producers/npm_audit/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // ParsingError indicates that a given string could not be parsed as a 8 | // particular audit report type. 9 | type ParsingError struct { 10 | Type string 11 | PrintableType string 12 | Err error 13 | } 14 | 15 | func (e *ParsingError) Error() string { 16 | return fmt.Sprintf("failed to parse input as %s", e.PrintableType) 17 | } 18 | 19 | func (e *ParsingError) Unwrap() error { 20 | return e.Err 21 | } 22 | 23 | // FormatError indicates that a given string could be parsed as a particular 24 | // audit report type, but does not satisfy the required format for that type of 25 | // audit report. 26 | type FormatError struct { 27 | Type string 28 | PrintableType string 29 | } 30 | 31 | func (e *FormatError) Error() string { 32 | return fmt.Sprintf("input is not %s", e.PrintableType) 33 | } 34 | -------------------------------------------------------------------------------- /producers/npm_audit/types/npmfullaudit/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "npmfullaudit", 3 | srcs = [ 4 | "npmfullaudit.go", 5 | ], 6 | visibility = ["//producers/npm_audit/..."], 7 | deps = [ 8 | "//api/proto:v1", 9 | "//producers", 10 | "//producers/npm_audit/types", 11 | ], 12 | ) 13 | 14 | go_test( 15 | name = "npmfullaudit_test", 16 | srcs = [ 17 | "npmfullaudit_test.go", 18 | ], 19 | deps = [ 20 | ":npmfullaudit", 21 | "//api/proto:v1", 22 | "//producers", 23 | "//third_party/go:stretchr_testify", 24 | ], 25 | ) 26 | -------------------------------------------------------------------------------- /producers/npm_audit/types/npmquickaudit/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "npmquickaudit", 3 | srcs = [ 4 | "npm_advisory.go", 5 | "npmquickaudit.go", 6 | ], 7 | visibility = ["//producers/npm_audit/..."], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | "//producers/npm_audit/types", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "npmquickaudit_test", 17 | srcs = [ 18 | "npm_advisory_test.go", 19 | "npmquickaudit_test.go", 20 | ], 21 | data = [ 22 | ":npmquickaudit_test_data", 23 | ], 24 | deps = [ 25 | ":npmquickaudit", 26 | "//api/proto:v1", 27 | "//producers", 28 | "//third_party/go:h2non_gock", 29 | "//third_party/go:stretchr_testify", 30 | ], 31 | ) 32 | 33 | filegroup( 34 | name = "npmquickaudit_test_data", 35 | srcs = [ 36 | "npm_advisory_1556", 37 | "npm_advisory_no_advisorydata", 38 | "npm_advisory_not_json", 39 | ], 40 | visibility = [":npmquickaudit_test"], 41 | ) 42 | -------------------------------------------------------------------------------- /producers/npm_audit/types/npmquickaudit/npm_advisory_no_advisorydata: -------------------------------------------------------------------------------- 1 | {"noAdvisoryData":true} 2 | -------------------------------------------------------------------------------- /producers/npm_audit/types/npmquickaudit/npm_advisory_not_json: -------------------------------------------------------------------------------- 1 | The npm Registry should return JSON content in the response body. 2 | 3 | This file is part of a test that ensures correct behaviour if the response is not JSON-encoded. 4 | -------------------------------------------------------------------------------- /producers/npm_audit/types/types.go: -------------------------------------------------------------------------------- 1 | // Package types provides common types for audit report formats. 2 | package types 3 | 4 | import ( 5 | v1 "github.com/thought-machine/dracon/api/proto/v1" 6 | ) 7 | 8 | // Report is an interface for audit report formats. 9 | type Report interface { 10 | // SetPackagePath sets the path to the package to which this audit report 11 | // belongs. 12 | SetPackagePath(string) 13 | 14 | // Type returns a short textual description of this report type. 15 | Type() string 16 | 17 | // AsIssues transforms this Report into a slice of Dracon Issues that can be 18 | // processed by the Dracon enricher. 19 | AsIssues() []*v1.Issue 20 | } 21 | -------------------------------------------------------------------------------- /producers/pipsafety/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "pipsafety", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | "//producers/pipsafety/types", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "pipsafety-test", 17 | srcs = [ 18 | "main.go", 19 | "main_test.go", 20 | ], 21 | deps = [ 22 | "//api/proto:v1", 23 | "//producers", 24 | "//producers/pipsafety/types", 25 | "//third_party/go:stretchr_testify", 26 | ], 27 | ) 28 | 29 | docker_image( 30 | name = "image", 31 | srcs = [ 32 | ":pipsafety", 33 | ], 34 | base_image = "//build/docker:dracon-base-go", 35 | image = "dracon-producer-pipsafety", 36 | ) 37 | -------------------------------------------------------------------------------- /producers/pipsafety/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY pipsafety /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/pipsafety/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | v1 "github.com/thought-machine/dracon/api/proto/v1" 8 | "github.com/thought-machine/dracon/producers" 9 | "github.com/thought-machine/dracon/producers/pipsafety/types" 10 | ) 11 | 12 | func parseIssues(out []types.SafetyIssue) []*v1.Issue { 13 | issues := []*v1.Issue{} 14 | for _, r := range out { 15 | issues = append(issues, &v1.Issue{ 16 | Target: r.Name, 17 | Type: "Vulnerable Dependency", 18 | Title: fmt.Sprintf("%s%s", r.Name, r.VersionConstraint), 19 | Severity: v1.Severity_SEVERITY_MEDIUM, 20 | Cvss: 0.0, 21 | Confidence: v1.Confidence_CONFIDENCE_MEDIUM, 22 | Description: fmt.Sprintf("%s\nCurrent Version: %s", r.Description, r.CurrentVersion), 23 | Cve: r.Cve, 24 | }) 25 | } 26 | return issues 27 | } 28 | 29 | func main() { 30 | if err := producers.ParseFlags(); err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | inFile, err := producers.ReadInFile() 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | issues := []types.SafetyIssue{} 40 | if err := producers.ParseJSON(inFile, &issues); err != nil { 41 | log.Fatal(err) 42 | } 43 | 44 | if err := producers.WriteDraconOut( 45 | "pipsafety", 46 | parseIssues(issues), 47 | ); err != nil { 48 | log.Fatal(err) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /producers/pipsafety/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "safety-issue.go", 5 | ], 6 | visibility = ["//producers/pipsafety/..."], 7 | ) 8 | 9 | go_test( 10 | name = "types-test", 11 | srcs = [ 12 | "safety-issue.go", 13 | "safety-issue_test.go", 14 | ], 15 | deps = [ 16 | "//third_party/go:stretchr_testify", 17 | ], 18 | ) 19 | -------------------------------------------------------------------------------- /producers/pipsafety/types/safety-issue.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "encoding/json" 5 | "regexp" 6 | ) 7 | 8 | // SafetyIssue represents a pip-safety finding 9 | type SafetyIssue struct { 10 | Name string 11 | VersionConstraint string 12 | CurrentVersion string 13 | Description string 14 | Cve string 15 | } 16 | 17 | //UnmarshalJSON is autocalled on any JSON unmarshalling into the SafetyIssue struct 18 | // read semi-unstructured safety json into struct 19 | func (i *SafetyIssue) UnmarshalJSON(data []byte) error { 20 | 21 | var v []interface{} 22 | if err := json.Unmarshal(data, &v); err != nil { 23 | return err 24 | } 25 | re := regexp.MustCompile(`CVE-\d{4}-\d+`) 26 | match := re.FindStringSubmatch(string(data)) 27 | cve := "" 28 | if len(match) > 0 { 29 | cve = match[0] 30 | } 31 | i.Name, _ = v[0].(string) 32 | i.VersionConstraint, _ = v[1].(string) 33 | i.CurrentVersion, _ = v[2].(string) 34 | i.Description, _ = v[3].(string) 35 | i.Cve = cve 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /producers/pipsafety/types/safety-issue_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | const exampleOutput = `[ 11 | [ 12 | "aegea", 13 | "<2.2.7", 14 | "2.2.6", 15 | "Aegea 2.2.7 avoids CVE-2018-1000805.", 16 | "37611" 17 | ],[ 18 | "aegea", 19 | "<2.2.7", 20 | "2.2.6", 21 | "Aegea 2.2.7 avoids CVE-2018-1000805.", 22 | "37611" 23 | ] 24 | ] 25 | ` 26 | 27 | func TestUnmarshalJSON(t *testing.T) { 28 | expectedOutput := []SafetyIssue{{ 29 | Name: "aegea", 30 | CurrentVersion: "2.2.6", 31 | Description: "Aegea 2.2.7 avoids CVE-2018-1000805.", 32 | VersionConstraint: "<2.2.7", 33 | Cve: "CVE-2018-1000805", 34 | }, { 35 | Name: "aegea", 36 | CurrentVersion: "2.2.6", 37 | Description: "Aegea 2.2.7 avoids CVE-2018-1000805.", 38 | VersionConstraint: "<2.2.7", 39 | Cve: "CVE-2018-1000805", 40 | }} 41 | safetyIssues := []SafetyIssue{} 42 | err := json.Unmarshal([]byte(exampleOutput), &safetyIssues) 43 | assert.Nil(t, err) 44 | assert.Equal(t, safetyIssues, expectedOutput) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /producers/producer_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import tempfile 4 | import unittest 5 | 6 | import producers.producer as shared 7 | 8 | from gen import engine_pb2 9 | from gen import issue_pb2 10 | 11 | 12 | class TestProducer(unittest.TestCase): 13 | 14 | def test_write_dracon_out(self): 15 | with tempfile.NamedTemporaryFile() as fp: 16 | args = shared.parse_flags(['-out', fp.name, '-in', 'foo']) 17 | shared.write_dracon_out(args, "foo", [ 18 | issue_pb2.Issue( 19 | target="/dracon/source/foobar", 20 | title="/dracon/source/barfoo", 21 | description="/dracon/source/example.yaml", 22 | cve="123-321", 23 | ) 24 | ]) 25 | fp.flush() 26 | res = fp.read() 27 | pblaunch = engine_pb2.LaunchToolResponse() 28 | pblaunch.ParseFromString(res) 29 | self.assertEqual(engine_pb2.LaunchToolResponse( 30 | tool_name="foo", 31 | issues=[issue_pb2.Issue( 32 | target="./foobar", 33 | title="./barfoo", 34 | description="./example.yaml", 35 | source="unknown", 36 | cve="123-321" 37 | )] 38 | ), pblaunch) 39 | 40 | 41 | if __name__ == '__main__': 42 | unittest.main() 43 | -------------------------------------------------------------------------------- /producers/python_bandit/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "python_bandit", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | ], 12 | ) 13 | 14 | go_test( 15 | name = "python_bandit_test", 16 | srcs = [ 17 | "main_test.go", 18 | ], 19 | ) 20 | 21 | docker_image( 22 | name = "dracon-producer-bandit", 23 | srcs = [ 24 | ":python_bandit", 25 | ], 26 | base_image = "//build/docker:dracon-base-go", 27 | image = "dracon-producer-bandit", 28 | visibility = ["//examples/..."], 29 | ) 30 | -------------------------------------------------------------------------------- /producers/python_bandit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY python_bandit /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/python_bandit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thought-machine/dracon/afa59ace9a311a5ab1f171450b720b7ac054365a/producers/python_bandit/__init__.py -------------------------------------------------------------------------------- /producers/python_bandit/bandit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | import sys 5 | import producers.producer as shared 6 | from gen import issue_pb2 7 | 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | def parse_tool_results(results: dict) -> [issue_pb2.Issue]: 13 | issues = [] 14 | for res in results['results']: 15 | iss = parse_issue(res) 16 | issues.append(iss) 17 | return issues 18 | 19 | 20 | def parse_issue(rec_issue: dict) -> issue_pb2.Issue: 21 | return issue_pb2.Issue( 22 | target=f"{rec_issue['filename']}:{rec_issue['line_range']}", 23 | type=rec_issue['test_name'], 24 | title=rec_issue['test_name'], 25 | severity=f"SEVERITY_{rec_issue['issue_severity']}", 26 | cvss=0, 27 | confidence=f"CONFIDENCE_{rec_issue['issue_confidence']}", 28 | description=rec_issue['issue_text'] 29 | ) 30 | 31 | 32 | if __name__ == "__main__": 33 | args = shared.parse_flags(sys.argv[1:]) 34 | tool_results = shared.parse_in_file_json(args) 35 | issues = parse_tool_results(tool_results) 36 | shared.write_dracon_out(args, "bandit", issues) 37 | -------------------------------------------------------------------------------- /producers/python_bandit/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | -------------------------------------------------------------------------------- /producers/sarif/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | # this producer covers any tool that supports the SARIF format https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=sarif 4 | go_binary( 5 | name = "sarif", 6 | srcs = [ 7 | "main.go", 8 | ], 9 | deps = [ 10 | "//api/proto:v1", 11 | "//producers", 12 | "//third_party/go:go-sarif", 13 | ], 14 | ) 15 | 16 | go_test( 17 | name = "sarif_test", 18 | srcs = [ 19 | "main.go", 20 | "main_test.go", 21 | ], 22 | deps = [ 23 | "//api/proto:v1", 24 | "//producers", 25 | "//third_party/go:go-sarif", 26 | "//third_party/go:stretchr_testify", 27 | ], 28 | ) 29 | 30 | docker_image( 31 | name = "dracon-producer-sarif", 32 | srcs = [ 33 | ":sarif", 34 | ], 35 | base_image = "//build/docker:dracon-base-go", 36 | image = "dracon-producer-sarif", 37 | ) 38 | -------------------------------------------------------------------------------- /producers/sarif/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY sarif /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/sarif/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | v1 "github.com/thought-machine/dracon/api/proto/v1" 5 | 6 | "log" 7 | 8 | "github.com/owenrumney/go-sarif/v2/sarif" 9 | "github.com/thought-machine/dracon/producers" 10 | ) 11 | 12 | func main() { 13 | if err := producers.ParseFlags(); err != nil { 14 | log.Fatal(err) 15 | } 16 | 17 | inFile, err := producers.ReadInFile() 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | results, err := sarif.FromString(string(inFile)) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | for _, run := range results.Runs { 27 | tool := run.Tool.Driver.Name 28 | if err := producers.WriteDraconOut(tool, parseOut(*run)); err != nil { 29 | log.Fatal(err) 30 | } 31 | } 32 | } 33 | 34 | func parseOut(run sarif.Run) []*v1.Issue { 35 | issues := []*v1.Issue{} 36 | for _, res := range run.Results { 37 | for _, loc := range res.Locations { 38 | target := loc.PhysicalLocation.ArtifactLocation.URI 39 | issues = append(issues, &v1.Issue{ 40 | Target: *target, 41 | Title: *res.RuleID, 42 | Description: *res.Message.Text, 43 | Type: "Security Automation Result", 44 | Severity: LevelToSeverity(*res.Level), 45 | Confidence: v1.Confidence_CONFIDENCE_MEDIUM, 46 | Cvss: 0, 47 | Cve: "", 48 | }) 49 | } 50 | } 51 | return issues 52 | } 53 | 54 | // LevelToSeverity transforms error, warning and note levels to high, medium and low respectively 55 | func LevelToSeverity(level string) v1.Severity { 56 | if level == "error" { 57 | return v1.Severity_SEVERITY_HIGH 58 | } else if level == "warning" { 59 | return v1.Severity_SEVERITY_MEDIUM 60 | } 61 | return v1.Severity_SEVERITY_LOW 62 | } 63 | -------------------------------------------------------------------------------- /producers/securityhub/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "securityhub", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | "//third_party/go:aws-sdk-go-v2-securityhub", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "securityhub_test", 17 | srcs = [ 18 | "main.go", 19 | "main_test.go", 20 | ], 21 | data = glob(["./testcases/*.json"]), 22 | deps = [ 23 | "//api/proto:v1", 24 | "//pkg/putil", 25 | "//producers", 26 | "//third_party/go:aws-sdk-go-v2-securityhub", 27 | "//third_party/go:stretchr_testify", 28 | ], 29 | ) 30 | 31 | docker_image( 32 | name = "dracon-producer-securityhub", 33 | srcs = [ 34 | ":securityhub", 35 | ], 36 | base_image = "//build/docker:dracon-base-go", 37 | image = "dracon-producer-securityhub", 38 | visibility = ["//examples/..."], 39 | ) 40 | -------------------------------------------------------------------------------- /producers/securityhub/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY securityhub /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/securityhub/testcases/empty_finding.json: -------------------------------------------------------------------------------- 1 | { 2 | "Findings": [{}] 3 | } -------------------------------------------------------------------------------- /producers/securityhub/testcases/no_findings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Findings": [] 3 | } -------------------------------------------------------------------------------- /producers/securityhub/testcases/unparseable.json: -------------------------------------------------------------------------------- 1 | . not 2 | valid }{ 3 | -- json 4 | 5 | 6 | ][ 7 | 8 | :) -------------------------------------------------------------------------------- /producers/semgrep/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "semgrep", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | "//producers/semgrep/types", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "semgrep_test", 17 | srcs = [ 18 | "main.go", 19 | "main_test.go", 20 | ], 21 | deps = [ 22 | "//api/proto:v1", 23 | "//producers", 24 | "//producers/semgrep/types", 25 | "//third_party/go:stretchr_testify", 26 | ], 27 | ) 28 | 29 | docker_image( 30 | name = "image", 31 | srcs = [ 32 | ":semgrep", 33 | ], 34 | base_image = "//build/docker:dracon-base-go", 35 | image = "dracon-producer-semgrep", 36 | ) 37 | -------------------------------------------------------------------------------- /producers/semgrep/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY semgrep /semgrep 4 | 5 | ENTRYPOINT ["/semgrep"] -------------------------------------------------------------------------------- /producers/semgrep/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | v1 "github.com/thought-machine/dracon/api/proto/v1" 8 | "github.com/thought-machine/dracon/producers/semgrep/types" 9 | 10 | "github.com/thought-machine/dracon/producers" 11 | ) 12 | 13 | func main() { 14 | if err := producers.ParseFlags(); err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | inFile, err := producers.ReadInFile() 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | var results types.SemgrepResults 24 | if err := producers.ParseJSON(inFile, &results); err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | issues := parseIssues(results) 29 | if err := producers.WriteDraconOut( 30 | "semgrep", 31 | issues, 32 | ); err != nil { 33 | log.Fatal(err) 34 | } 35 | } 36 | 37 | func parseIssues(out types.SemgrepResults) []*v1.Issue { 38 | issues := []*v1.Issue{} 39 | 40 | results := out.Results 41 | 42 | for _, r := range results { 43 | 44 | // Map the semgrep severity levels to dracon severity levels 45 | severityMap := map[string]v1.Severity{ 46 | "INFO": v1.Severity_SEVERITY_INFO, 47 | "WARNING": v1.Severity_SEVERITY_MEDIUM, 48 | "ERROR": v1.Severity_SEVERITY_HIGH, 49 | } 50 | 51 | sev := severityMap[r.Extra.Severity] 52 | 53 | issues = append(issues, &v1.Issue{ 54 | Target: fmt.Sprintf("%s:%v-%v", r.Path, r.Start.Line, r.End.Line), 55 | Type: r.Extra.Message, 56 | Title: r.CheckID, 57 | Severity: sev, 58 | Cvss: 0.0, 59 | Confidence: v1.Confidence_CONFIDENCE_MEDIUM, 60 | Description: r.Extra.Lines, 61 | }) 62 | } 63 | return issues 64 | } 65 | -------------------------------------------------------------------------------- /producers/semgrep/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "semgrep-issue.go", 5 | ], 6 | visibility = ["//producers/semgrep/..."], 7 | ) 8 | -------------------------------------------------------------------------------- /producers/semgrep/types/semgrep-issue.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Position represents where in the file the finding is located 4 | type Position struct { 5 | Col int `json:"col"` 6 | Line int `json:"line"` 7 | } 8 | 9 | // Extra contains extra info needed for semgrep issue 10 | type Extra struct { 11 | Message string `json:"message"` 12 | Metavars Metavars `json:"metavars"` 13 | Metadata Metadata `json:"metadata"` 14 | Severity string `json:"severity"` 15 | Lines string `json:"lines"` 16 | } 17 | 18 | // SemgrepIssue represents a semgrep issue 19 | type SemgrepIssue struct { 20 | CheckID string `json:"check_id"` 21 | Path string `json:"path"` 22 | Start Position `json:"start"` 23 | End Position `json:"end"` 24 | Extra Extra `json:"extra"` 25 | } 26 | 27 | // SemgrepResults represents a series of semgrep issues 28 | type SemgrepResults struct { 29 | Results []SemgrepIssue `'json:"results"` 30 | } 31 | 32 | // Metavars currently is empty but could represent more metavariables for semgrep 33 | type Metavars struct { 34 | } 35 | 36 | // Metadata currently is empty, however, could represent semgrep issue metadata going forward. 37 | type Metadata struct { 38 | } 39 | -------------------------------------------------------------------------------- /producers/typescript_eslint/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "typescript_eslint", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | "//producers/typescript_eslint/types", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "typescript_eslint_test", 17 | srcs = [ 18 | "main.go", 19 | "main_test.go", 20 | ], 21 | deps = [ 22 | "//api/proto:v1", 23 | "//producers", 24 | "//producers/typescript_eslint/types", 25 | "//third_party/go:stretchr_testify", 26 | ], 27 | ) 28 | 29 | docker_image( 30 | name = "dracon-producer-eslint", 31 | srcs = [ 32 | ":typescript_eslint", 33 | ], 34 | base_image = "//build/docker:dracon-base-go", 35 | image = "dracon-producer-eslint", 36 | ) 37 | -------------------------------------------------------------------------------- /producers/typescript_eslint/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY typescript_eslint /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/typescript_eslint/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | v1 "github.com/thought-machine/dracon/api/proto/v1" 8 | "github.com/thought-machine/dracon/producers/typescript_eslint/types" 9 | 10 | "github.com/thought-machine/dracon/producers" 11 | ) 12 | 13 | func main() { 14 | if err := producers.ParseFlags(); err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | inFile, err := producers.ReadInFile() 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | var results []types.ESLintIssue 24 | if err := producers.ParseJSON(inFile, &results); err != nil { 25 | log.Fatal(err) 26 | } 27 | issues := parseIssues(results) 28 | if err := producers.WriteDraconOut( 29 | "eslint", 30 | issues, 31 | ); err != nil { 32 | log.Fatal(err) 33 | } 34 | } 35 | 36 | func parseIssues(out []types.ESLintIssue) []*v1.Issue { 37 | issues := []*v1.Issue{} 38 | for _, r := range out { 39 | for _, msg := range r.Messages { 40 | sev := v1.Severity_SEVERITY_LOW 41 | if msg.Severity == 1 { 42 | sev = v1.Severity_SEVERITY_MEDIUM 43 | } else if msg.Severity == 2 { 44 | sev = v1.Severity_SEVERITY_HIGH 45 | } 46 | iss := &v1.Issue{ 47 | Target: fmt.Sprintf("%s:%v:%v", r.FilePath, msg.Line, msg.Column), 48 | Type: msg.RuleID, 49 | Title: msg.RuleID, 50 | Severity: sev, 51 | Cvss: 0.0, 52 | Confidence: v1.Confidence_CONFIDENCE_MEDIUM, 53 | Description: msg.Message, 54 | } 55 | issues = append(issues, iss) 56 | } 57 | } 58 | return issues 59 | } 60 | -------------------------------------------------------------------------------- /producers/typescript_eslint/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "eslint-issue.go", 5 | ], 6 | visibility = ["//producers/typescript_eslint/..."], 7 | ) 8 | -------------------------------------------------------------------------------- /producers/typescript_eslint/types/eslint-issue.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Message represents where in the file the finding is located and the details of the finding 4 | type Message struct { 5 | RuleID string `json:"ruleId"` 6 | Severity int `json:"severity"` 7 | Message string `json:"message"` 8 | Line int `json:"line"` 9 | Column int `json:"column"` 10 | } 11 | 12 | // ESLintIssue represents a ESLint Result 13 | type ESLintIssue struct { 14 | FilePath string `json:"filePath"` 15 | Messages []Message `json:"messages` 16 | } 17 | -------------------------------------------------------------------------------- /producers/typescript_tslint/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "typescript_tslint", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | "//producers/typescript_tslint/types", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "typescript_tslint_test", 17 | srcs = [ 18 | "main.go", 19 | "main_test.go", 20 | ], 21 | deps = [ 22 | "//api/proto:v1", 23 | "//producers", 24 | "//producers/typescript_tslint/types", 25 | "//third_party/go:stretchr_testify", 26 | ], 27 | ) 28 | 29 | docker_image( 30 | name = "dracon-producer-tslint", 31 | srcs = [ 32 | ":typescript_tslint", 33 | ], 34 | base_image = "//build/docker:dracon-base-go", 35 | image = "dracon-producer-tslint", 36 | ) 37 | -------------------------------------------------------------------------------- /producers/typescript_tslint/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY typescript_tslint /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/typescript_tslint/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | 8 | v1 "github.com/thought-machine/dracon/api/proto/v1" 9 | "github.com/thought-machine/dracon/producers/typescript_tslint/types" 10 | 11 | "github.com/thought-machine/dracon/producers" 12 | ) 13 | 14 | func main() { 15 | if err := producers.ParseFlags(); err != nil { 16 | log.Fatal(err) 17 | } 18 | 19 | inFile, err := producers.ReadInFile() 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | var results []types.TSLintIssue 25 | if err := producers.ParseJSON(inFile, &results); err != nil { 26 | log.Fatal(err) 27 | } 28 | issues := parseIssues(results) 29 | if err := producers.WriteDraconOut( 30 | "tslint", 31 | issues, 32 | ); err != nil { 33 | log.Fatal(err) 34 | } 35 | } 36 | 37 | func parseIssues(out []types.TSLintIssue) []*v1.Issue { 38 | issues := []*v1.Issue{} 39 | for _, r := range out { 40 | 41 | bytes, err := json.Marshal(r) 42 | if err != nil { 43 | log.Print("Couldn't write issue ", fmt.Sprintf("%s:%v-%v", r.Name, r.StartPosition.Line, r.EndPosition.Line)) 44 | } 45 | 46 | issues = append(issues, &v1.Issue{ 47 | Target: fmt.Sprintf("%s:%v-%v", r.Name, r.StartPosition.Line, r.EndPosition.Line), 48 | Type: r.RuleName, 49 | Title: r.Failure, 50 | Severity: v1.Severity_SEVERITY_MEDIUM, 51 | Cvss: 0.0, 52 | Confidence: v1.Confidence_CONFIDENCE_MEDIUM, 53 | Description: string(bytes), 54 | }) 55 | } 56 | return issues 57 | } 58 | -------------------------------------------------------------------------------- /producers/typescript_tslint/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "tslint-issue.go", 5 | ], 6 | visibility = ["//producers/typescript_tslint/..."], 7 | ) 8 | -------------------------------------------------------------------------------- /producers/typescript_tslint/types/tslint-issue.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Position represents where in the file the finding is located 4 | type Position struct { 5 | Character int `json:"character"` 6 | Line int `"json:line"` 7 | Position int `"json:position"` 8 | } 9 | 10 | // TSLintIssue represents a TSLint Result 11 | type TSLintIssue struct { 12 | RuleName string `json:"ruleName"` 13 | Failure string `json:"failure"` 14 | Name string `json:"name"` 15 | StartPosition Position `json:"startPosition"` 16 | EndPosition Position `json:"endPosition"` 17 | } 18 | -------------------------------------------------------------------------------- /producers/yarn_audit/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | # this producer covers yarn audit https://classic.yarnpkg.com/lang/en/docs/cli/audit/ 4 | 5 | go_binary( 6 | name = "yarn_audit", 7 | srcs = [ 8 | "main.go", 9 | ], 10 | deps = [ 11 | "//api/proto:v1", 12 | "//producers", 13 | "//producers/yarn_audit/types", 14 | ], 15 | ) 16 | 17 | docker_image( 18 | name = "dracon-producer-yarn-audit", 19 | srcs = [ 20 | ":yarn_audit", 21 | ], 22 | base_image = "//build/docker:dracon-base-go", 23 | image = "dracon-producer-yarn-audit", 24 | ) 25 | -------------------------------------------------------------------------------- /producers/yarn_audit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY yarn_audit /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/yarn_audit/README.md: -------------------------------------------------------------------------------- 1 | For use with output of `yarn audit --json | jq -cs .` 2 | -------------------------------------------------------------------------------- /producers/yarn_audit/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/thought-machine/dracon/producers" 5 | "github.com/thought-machine/dracon/producers/yarn_audit/types" 6 | 7 | "log" 8 | ) 9 | 10 | func main() { 11 | if err := producers.ParseFlags(); err != nil { 12 | log.Fatal(err) 13 | } 14 | 15 | in, err := producers.ReadInFile() 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | 20 | yarnReport, err := types.NewReport(in) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | if err := producers.WriteDraconOut( 26 | "yarn-audit", 27 | yarnReport.AuditAdvisories.AsIssues(), 28 | ); err != nil { 29 | log.Fatal(err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /producers/yarn_audit/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "yarn-issue.go", 5 | ], 6 | visibility = ["//producers/yarn_audit/..."], 7 | deps = [ 8 | "//api/proto:v1", 9 | "//producers", 10 | ], 11 | ) 12 | 13 | go_test( 14 | name = "types_test", 15 | srcs = [ 16 | "yarn-issue_test.go", 17 | ], 18 | deps = [ 19 | ":types", 20 | "//api/proto:v1", 21 | "//producers", 22 | "//third_party/go:stretchr_testify", 23 | ], 24 | ) 25 | -------------------------------------------------------------------------------- /producers/zap_producer/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | go_binary( 4 | name = "zap", 5 | srcs = [ 6 | "main.go", 7 | ], 8 | deps = [ 9 | "//api/proto:v1", 10 | "//producers", 11 | "//producers/zap_producer/types", 12 | ], 13 | ) 14 | 15 | go_test( 16 | name = "zap_test", 17 | srcs = [ 18 | "main.go", 19 | "main_test.go", 20 | ], 21 | deps = [ 22 | "//api/proto:v1", 23 | "//producers", 24 | "//producers/zap_producer/types", 25 | "//third_party/go:stretchr_testify", 26 | ], 27 | ) 28 | 29 | docker_image( 30 | name = "dracon-producer-zap", 31 | srcs = [ 32 | ":zap", 33 | ], 34 | base_image = "//build/docker:dracon-base-go", 35 | image = "dracon-producer-zap", 36 | ) 37 | -------------------------------------------------------------------------------- /producers/zap_producer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM //build/docker:dracon-base-go 2 | 3 | COPY zap /parse 4 | 5 | ENTRYPOINT ["/parse"] 6 | -------------------------------------------------------------------------------- /producers/zap_producer/types/BUILD: -------------------------------------------------------------------------------- 1 | go_library( 2 | name = "types", 3 | srcs = [ 4 | "zap-issue.go", 5 | ], 6 | visibility = ["//producers/zap_producer/..."], 7 | ) 8 | -------------------------------------------------------------------------------- /producers/zap_producer/types/zap-issue.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // ZapOut represents the output of a zap scan 4 | type ZapOut struct { 5 | Version string `json:"@version"` 6 | Generated string `json:"@generated"` 7 | Site []ZapSites `json:"site"` 8 | } 9 | 10 | // ZapSites represents a zap site section 11 | type ZapSites struct { 12 | Name string `json:"@name"` 13 | Host string `json:"@host"` 14 | Port string `json:"@port"` 15 | Ssl string `json:"@ssl"` 16 | Alerts []ZapAlerts `json:"alerts"` 17 | } 18 | 19 | // ZapInstances represents a zap occurrence for a specific alert 20 | type ZapInstances struct { 21 | URI string `json:"uri"` 22 | Method string `json:"method"` 23 | } 24 | 25 | // ZapAlerts represents a zap vulnerability 26 | type ZapAlerts struct { 27 | PluginID string `json:"Id"` 28 | AlertRef string `json:"alertRef"` 29 | Alert string `json:"alert"` 30 | Name string `json:"name"` 31 | RiskCode string `json:"riskcode"` 32 | Confidence string `json:"confidence"` 33 | RiskDesc string `json:"riskdesc"` 34 | Description string `json:"desc"` 35 | Instances []ZapInstances `json:"instances"` 36 | Count string `json:"count"` 37 | Solution string `json:"solution"` 38 | OtherInfo string `json:"otherinfo"` 39 | Reference string `json:"reference"` 40 | CweID string `json:"cweid"` 41 | WascID string `json:"wascid"` 42 | SourceID string `json:"sourceid"` 43 | } 44 | -------------------------------------------------------------------------------- /resources/patches/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "patches", 3 | srcs = glob(["*.yaml"]), 4 | visibility = ["//pkg/template/..."], 5 | ) 6 | -------------------------------------------------------------------------------- /resources/patches/patch-dracon-enriched.Consumer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Enriched input 3 | - op: add 4 | path: /spec/inputs/resources/- 5 | value: {name: enricher, type: storage} 6 | 7 | - op: replace 8 | path: /apiVersion 9 | value: tekton.dev/v1alpha1 10 | - op: replace 11 | path: /kind 12 | value: Task 13 | 14 | - op: add 15 | path: /metadata/labels/project 16 | value: dracon 17 | 18 | {{range .PipelineParams}} 19 | - op: add 20 | path: /spec/inputs/params/- 21 | value: {name: {{.Name}}, type: {{.Type}}} 22 | {{end}} 23 | 24 | {{range .PipelineParams}} 25 | - op: add 26 | path: /spec/steps/*/env/- 27 | value: {name: {{.Name}}, value: $(inputs.params.{{.Name}})} 28 | {{end}} 29 | -------------------------------------------------------------------------------- /resources/patches/patch-dracon.Enricher.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - op: replace 3 | path: /apiVersion 4 | value: tekton.dev/v1alpha1 5 | - op: replace 6 | path: /kind 7 | value: Task 8 | 9 | - op: add 10 | path: /metadata/labels/project 11 | value: dracon 12 | 13 | - op: add 14 | path: /spec/outputs/resources/- 15 | value: {name: enricher, type: storage} 16 | 17 | - op: add 18 | path: /spec/steps/0 19 | value: 20 | name: setup-permissions 21 | image: busybox:latest 22 | command: ["chown"] 23 | args: ["-R", "1000:1000", "/workspace/output/enricher"] 24 | 25 | 26 | {{ $producers := .PipelineTaskProducers }} 27 | {{range $p := $producers}} 28 | - op: add 29 | path: /spec/inputs/resources/- 30 | value: {name: {{$p.Name}}-producer, type: storage} 31 | {{end}} 32 | -------------------------------------------------------------------------------- /resources/patches/patch-dracon.PipelineResource.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - op: replace 3 | path: /apiVersion 4 | value: tekton.dev/v1alpha1 5 | 6 | - op: add 7 | path: /metadata/labels/project 8 | value: dracon 9 | -------------------------------------------------------------------------------- /resources/patches/patch-dracon.PipelineRun.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - op: replace 3 | path: /apiVersion 4 | value: tekton.dev/v1alpha1 5 | 6 | - op: add 7 | path: /metadata/labels/project 8 | value: dracon 9 | 10 | # generate pipeline resources 11 | {{ $pipelineParams := .PipelineParams}} 12 | {{ $enrichers := .PipelineTaskEnrichers }} 13 | {{ $producers := .PipelineTaskProducers }} 14 | {{ $consumers := .PipelineTaskConsumers }} 15 | {{ $runID := .RunID }} 16 | 17 | {{range $pp := $pipelineParams}} 18 | - op: add 19 | path: /spec/params/- 20 | value: {name: {{$pp.Name}}, value: "{{$pp.Value}}"} 21 | {{end}} 22 | 23 | - op: add 24 | path: /spec/resources/- 25 | value: {name: source, resourceRef: {name: "{{$runID}}-source"}} 26 | 27 | {{range $p := $producers}} 28 | - op: add 29 | path: /spec/resources/- 30 | value: {name: {{$p.Name}}-producer, resourceRef: {name: "{{$runID}}-{{$p.Name}}"}} 31 | {{end}} 32 | 33 | {{range $e := $enrichers}} 34 | - op: add 35 | path: /spec/resources/- 36 | value: {name: {{$e.Name}}, resourceRef: {name: "{{$runID}}-{{$e.Name}}"}} 37 | {{end}} 38 | -------------------------------------------------------------------------------- /resources/patches/patch-dracon.Producer.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - op: replace 3 | path: /apiVersion 4 | value: tekton.dev/v1alpha1 5 | - op: replace 6 | path: /kind 7 | value: Task 8 | 9 | - op: add 10 | path: /metadata/labels/project 11 | value: dracon 12 | 13 | - op: add 14 | path: /spec/inputs/resources/- 15 | value: {name: source, type: storage} 16 | 17 | - op: add 18 | path: /spec/outputs/resources/- 19 | value: {name: producer, type: storage} 20 | 21 | - op: add 22 | path: /spec/volumes/- 23 | value: {emptyDir: {}, name: dracon-ws} 24 | 25 | - op: add 26 | path: /spec/steps/*/volumeMounts/- 27 | value: {mountPath: /dracon, name: dracon-ws} 28 | 29 | - op: add 30 | path: /spec/steps/0 31 | value: 32 | name: extract-source 33 | image: busybox:latest 34 | command: ["sh"] 35 | args: 36 | - "-c" 37 | - "mkdir -p {{.ProducerSourcePath}} && tar -C {{.ProducerSourcePath}} -xzf /workspace/source/source.tgz && chown -R 1000:1000 /workspace/output/producer /dracon" 38 | env: [] 39 | volumeMounts: [{mountPath: /dracon, name: dracon-ws}] 40 | 41 | {{range .PipelineParams}} 42 | - op: add 43 | path: /spec/steps/*/env/- 44 | value: {name: {{.Name}}, value: $(inputs.params.{{.Name}})} 45 | - op: add 46 | path: /spec/inputs/params/- 47 | value: {name: {{.Name}}, type: "string"} 48 | {{end}} 49 | -------------------------------------------------------------------------------- /resources/persistence/elasticsearch-kibana/elasticsearch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: elasticsearch 5 | spec: 6 | serviceName: "elasticsearch" 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: elasticsearch 11 | template: 12 | metadata: 13 | labels: 14 | app: elasticsearch 15 | spec: 16 | containers: 17 | - name: elasticsearch 18 | image: docker.elastic.co/elasticsearch/elasticsearch:7.6.0 19 | env: 20 | - name: discovery.type 21 | value: single-node 22 | ports: 23 | - containerPort: 9200 24 | name: client 25 | - containerPort: 9300 26 | name: nodes 27 | volumeMounts: 28 | - mountPath: /usr/share/elasticsearch/data 29 | name: elasticsearch 30 | subPath: es-data 31 | volumeClaimTemplates: 32 | - metadata: 33 | name: elasticsearch 34 | spec: 35 | accessModes: ["ReadWriteOnce"] 36 | storageClassName: "standard" 37 | resources: 38 | requests: 39 | storage: 1Gi 40 | --- 41 | apiVersion: v1 42 | kind: Service 43 | metadata: 44 | name: elasticsearch 45 | labels: 46 | service: elasticsearch 47 | spec: 48 | ports: 49 | - port: 9200 50 | name: client 51 | - port: 9300 52 | name: nodes 53 | selector: 54 | app: elasticsearch 55 | -------------------------------------------------------------------------------- /resources/persistence/elasticsearch-kibana/kibana.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: kibana-deployment 5 | labels: 6 | app: kibana 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: kibana 12 | template: 13 | metadata: 14 | labels: 15 | app: kibana 16 | spec: 17 | containers: 18 | - name: kibana 19 | image: docker.elastic.co/kibana/kibana:7.6.0 20 | ports: 21 | - containerPort: 5601 22 | name: webinterface 23 | --- 24 | apiVersion: v1 25 | kind: Service 26 | metadata: 27 | name: kibana 28 | labels: 29 | service: kibana 30 | spec: 31 | type: NodePort 32 | ports: 33 | - port: 5601 34 | name: webinterface 35 | selector: 36 | app: kibana 37 | -------------------------------------------------------------------------------- /resources/persistence/enricher-db/k8s.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | name: dracon-enrichment-db 6 | labels: 7 | app: dracon-enrichment-db 8 | project: dracon 9 | spec: 10 | revisionHistoryLimit: 3 11 | serviceName: dracon-enrichment-db 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app: dracon-enrichment-db 16 | project: dracon 17 | template: 18 | metadata: 19 | labels: 20 | app: dracon-enrichment-db 21 | project: dracon 22 | spec: 23 | securityContext: 24 | fsGroup: 70 25 | containers: 26 | - name: dracon-enrichment-db 27 | image: postgres:alpine 28 | env: 29 | - name: POSTGRES_USER 30 | value: dracon 31 | - name: POSTGRES_PASSWORD 32 | value: dracon 33 | resources: 34 | requests: 35 | memory: 512Mi 36 | cpu: 0.5 37 | limits: 38 | memory: 5Gi 39 | cpu: 1 40 | ports: 41 | - containerPort: 5432 42 | securityContext: 43 | runAsUser: 70 44 | runAsGroup: 70 45 | volumeMounts: 46 | - mountPath: /var/lib/postgresql 47 | name: dracon-postgres 48 | subPath: postgres-db 49 | volumeClaimTemplates: 50 | - metadata: 51 | name: dracon-postgres 52 | spec: 53 | accessModes: ["ReadWriteOnce"] 54 | storageClassName: "standard" 55 | resources: 56 | requests: 57 | storage: 1Gi 58 | --- 59 | apiVersion: v1 60 | kind: Service 61 | metadata: 62 | name: dracon-enrichment-db 63 | labels: 64 | project: dracon 65 | spec: 66 | ports: 67 | - port: 5432 68 | selector: 69 | app: dracon-enrichment-db 70 | -------------------------------------------------------------------------------- /resources/persistence/minio-storage/k8s.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: minio 6 | labels: 7 | project: dracon 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: minio 12 | strategy: 13 | type: Recreate 14 | template: 15 | metadata: 16 | labels: 17 | app: minio 18 | spec: 19 | containers: 20 | - name: minio 21 | image: minio/minio:RELEASE.2019-10-12T01-39-57Z 22 | command: ["sh"] 23 | args: ["-c", "mkdir -p /data/dracon && /usr/bin/minio server /data"] 24 | env: 25 | - name: MINIO_ACCESS_KEY 26 | value: "minio" 27 | - name: MINIO_SECRET_KEY 28 | value: "minio123" 29 | ports: 30 | - containerPort: 9000 31 | readinessProbe: 32 | httpGet: 33 | path: /minio/health/ready 34 | port: 9000 35 | initialDelaySeconds: 5 36 | periodSeconds: 20 37 | livenessProbe: 38 | httpGet: 39 | path: /minio/health/live 40 | port: 9000 41 | initialDelaySeconds: 5 42 | periodSeconds: 20 43 | --- 44 | apiVersion: v1 45 | kind: Service 46 | metadata: 47 | name: minio-storage 48 | spec: 49 | type: ClusterIP 50 | ports: 51 | - port: 9000 52 | selector: 53 | app: minio 54 | --- 55 | apiVersion: v1 56 | kind: Secret 57 | metadata: 58 | name: dracon-storage 59 | type: Opaque 60 | data: 61 | # Generated by: 62 | # cat templates/resources/minio-storage/minio.conf | base64 -w 0 63 | boto_config: W0NyZWRlbnRpYWxzXQphd3NfYWNjZXNzX2tleV9pZCA9IG1pbmlvCmF3c19zZWNyZXRfYWNjZXNzX2tleSA9IG1pbmlvMTIzCnMzX3BvcnQgPSA5MDAwCltzM10KaG9zdCA9IG1pbmlvLXN0b3JhZ2UuZHJhY29uLnN2YwpjYWxsaW5nX2Zvcm1hdCA9IGJvdG8uczMuY29ubmVjdGlvbi5PcmRpbmFyeUNhbGxpbmdGb3JtYXQKW0JvdG9dCmh0dHBzX3ZhbGlkYXRlX2NlcnRpZmljYXRlcyA9IEZhbHNlCmlzX3NlY3VyZSA9IEZhbHNlCltHU1V0aWxdCnByZWZlcl9hcGkgPSB4bWwK 64 | -------------------------------------------------------------------------------- /resources/persistence/minio-storage/minio.conf: -------------------------------------------------------------------------------- 1 | [Credentials] 2 | aws_access_key_id = minio 3 | aws_secret_access_key = minio123 4 | s3_port = 9000 5 | [s3] 6 | host = minio-storage.dracon.svc 7 | calling_format = boto.s3.connection.OrdinaryCallingFormat 8 | [Boto] 9 | https_validate_certificates = False 10 | is_secure = False 11 | [GSUtil] 12 | prefer_api = xml 13 | -------------------------------------------------------------------------------- /scripts/BUILD: -------------------------------------------------------------------------------- 1 | genrule( 2 | name = "default_docker_repo", 3 | outs = ["default_docker_repo"], 4 | cmd = f'echo -n "{CONFIG.DEFAULT_DOCKER_REPO}" > $OUTS', 5 | ) 6 | 7 | for script in glob(["*.sh"]): 8 | sh_binary( 9 | name = basename(splitext(script)[0]), 10 | data = [ 11 | ":default_docker_repo", 12 | ], 13 | main = script, 14 | deps = [ 15 | "//third_party/sh:shflags", 16 | ], 17 | ) 18 | -------------------------------------------------------------------------------- /scripts/clean-up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function deleteAll() { 4 | local resource=$1 5 | kubectl get $resource -l project=dracon -o custom-columns=NAME:.metadata.name | tail -n+2 | xargs kubectl delete $resource 6 | } 7 | 8 | deleteAll "pipelineruns.tekton.dev" 9 | deleteAll "pipelineresources.tekton.dev" 10 | deleteAll "taskruns.tekton.dev" 11 | deleteAll "pipelines.tekton.dev" 12 | deleteAll "tasks.tekton.dev" 13 | -------------------------------------------------------------------------------- /scripts/development/k8s/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "namespace", 3 | srcs = ["namespace.yaml"], 4 | ) 5 | -------------------------------------------------------------------------------- /scripts/development/k8s/elasticsearch-kibana/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "elasticsearch-kibana", 3 | srcs = [ 4 | "elasticsearch.yaml", 5 | "kibana.yaml", 6 | ], 7 | ) 8 | -------------------------------------------------------------------------------- /scripts/development/k8s/elasticsearch-kibana/elasticsearch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: elasticsearch 5 | namespace: dracon 6 | spec: 7 | serviceName: "elasticsearch" 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: elasticsearch 12 | template: 13 | metadata: 14 | labels: 15 | app: elasticsearch 16 | spec: 17 | containers: 18 | - name: elasticsearch 19 | image: docker.elastic.co/elasticsearch/elasticsearch:8.2.3 20 | env: 21 | - name: discovery.type 22 | value: single-node 23 | - name: xpack.security.enabled 24 | value: "false" 25 | ports: 26 | - containerPort: 9200 27 | name: client 28 | - containerPort: 9300 29 | name: nodes 30 | volumeMounts: 31 | - mountPath: /usr/share/elasticsearch/data 32 | name: elasticsearch 33 | subPath: es-data 34 | volumeClaimTemplates: 35 | - metadata: 36 | name: elasticsearch 37 | spec: 38 | accessModes: ["ReadWriteOnce"] 39 | storageClassName: "standard" 40 | resources: 41 | requests: 42 | storage: 1Gi 43 | --- 44 | apiVersion: v1 45 | kind: Service 46 | metadata: 47 | name: elasticsearch 48 | namespace: dracon 49 | labels: 50 | service: elasticsearch 51 | spec: 52 | ports: 53 | - port: 9200 54 | name: client 55 | - port: 9300 56 | name: nodes 57 | selector: 58 | app: elasticsearch 59 | -------------------------------------------------------------------------------- /scripts/development/k8s/elasticsearch-kibana/kibana.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: kibana-deployment 5 | namespace: dracon 6 | labels: 7 | app: kibana 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: kibana 13 | template: 14 | metadata: 15 | labels: 16 | app: kibana 17 | spec: 18 | containers: 19 | - name: kibana 20 | image: docker.elastic.co/kibana/kibana:8.2.3 21 | ports: 22 | - containerPort: 5601 23 | name: webinterface 24 | --- 25 | apiVersion: v1 26 | kind: Service 27 | metadata: 28 | name: kibana 29 | namespace: dracon 30 | labels: 31 | service: kibana 32 | spec: 33 | ports: 34 | - port: 5601 35 | name: webinterface 36 | selector: 37 | app: kibana 38 | --- 39 | apiVersion: networking.k8s.io/v1beta1 40 | kind: Ingress 41 | metadata: 42 | name: kibana 43 | namespace: dracon 44 | annotations: 45 | kubernetes.io/ingress.class: nginx 46 | nginx.ingress.kubernetes.io/backend-protocol: "http" 47 | spec: 48 | backend: 49 | serviceName: kibana 50 | servicePort: webinterface 51 | tls: 52 | - hosts: 53 | - "kibana.localhost" 54 | secretName: tls-kibana 55 | rules: 56 | - host: "kibana.localhost" 57 | http: 58 | paths: 59 | - backend: 60 | serviceName: kibana 61 | servicePort: webinterface 62 | -------------------------------------------------------------------------------- /scripts/development/k8s/enricher-db/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "enricher-db", 3 | srcs = ["k8s.yaml"], 4 | visibility = ["//scripts/development/..."], 5 | ) 6 | -------------------------------------------------------------------------------- /scripts/development/k8s/enricher-db/k8s.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | name: dracon-enrichment-db 6 | namespace: dracon 7 | labels: 8 | app: dracon-enrichment-db 9 | project: dracon 10 | spec: 11 | revisionHistoryLimit: 3 12 | serviceName: dracon-enrichment-db 13 | replicas: 1 14 | selector: 15 | matchLabels: 16 | app: dracon-enrichment-db 17 | project: dracon 18 | template: 19 | metadata: 20 | labels: 21 | app: dracon-enrichment-db 22 | project: dracon 23 | spec: 24 | securityContext: 25 | fsGroup: 70 26 | containers: 27 | - name: dracon-enrichment-db 28 | image: postgres:alpine 29 | env: 30 | - name: POSTGRES_USER 31 | value: dracon 32 | - name: POSTGRES_PASSWORD 33 | value: dracon 34 | resources: 35 | requests: 36 | memory: 512Mi 37 | cpu: 0.5 38 | limits: 39 | memory: 5Gi 40 | cpu: 1 41 | ports: 42 | - containerPort: 5432 43 | securityContext: 44 | runAsUser: 70 45 | runAsGroup: 70 46 | volumeMounts: 47 | - mountPath: /var/lib/postgresql 48 | name: dracon-postgres 49 | subPath: postgres-db 50 | volumeClaimTemplates: 51 | - metadata: 52 | name: dracon-postgres 53 | spec: 54 | accessModes: ["ReadWriteOnce"] 55 | storageClassName: "standard" 56 | resources: 57 | requests: 58 | storage: 1Gi 59 | --- 60 | apiVersion: v1 61 | kind: Service 62 | metadata: 63 | name: dracon-enrichment-db 64 | namespace: dracon 65 | labels: 66 | project: dracon 67 | spec: 68 | ports: 69 | - port: 5432 70 | selector: 71 | app: dracon-enrichment-db 72 | -------------------------------------------------------------------------------- /scripts/development/k8s/minio-storage/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "minio-storage", 3 | srcs = ["k8s.yaml"], 4 | ) 5 | -------------------------------------------------------------------------------- /scripts/development/k8s/minio-storage/k8s.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: minio 6 | namespace: dracon 7 | labels: 8 | project: dracon 9 | spec: 10 | selector: 11 | matchLabels: 12 | app: minio 13 | strategy: 14 | type: Recreate 15 | template: 16 | metadata: 17 | labels: 18 | app: minio 19 | spec: 20 | containers: 21 | - name: minio 22 | image: minio/minio:RELEASE.2019-10-12T01-39-57Z 23 | command: ["sh"] 24 | args: ["-c", "mkdir -p /data/dracon && /usr/bin/minio server /data"] 25 | env: 26 | - name: MINIO_ACCESS_KEY 27 | value: "minio" 28 | - name: MINIO_SECRET_KEY 29 | value: "minio123" 30 | ports: 31 | - containerPort: 9000 32 | readinessProbe: 33 | httpGet: 34 | path: /minio/health/ready 35 | port: 9000 36 | initialDelaySeconds: 5 37 | periodSeconds: 20 38 | livenessProbe: 39 | httpGet: 40 | path: /minio/health/live 41 | port: 9000 42 | initialDelaySeconds: 5 43 | periodSeconds: 20 44 | --- 45 | apiVersion: v1 46 | kind: Service 47 | metadata: 48 | name: minio-storage 49 | namespace: dracon 50 | spec: 51 | type: ClusterIP 52 | ports: 53 | - port: 9000 54 | selector: 55 | app: minio 56 | --- 57 | apiVersion: v1 58 | kind: Secret 59 | metadata: 60 | name: dracon-storage 61 | namespace: dracon 62 | type: Opaque 63 | data: 64 | # Generated by: 65 | # cat scripts/development/k8s/minio-storage/minio.conf | base64 -w 0 66 | boto_config: W0NyZWRlbnRpYWxzXQphd3NfYWNjZXNzX2tleV9pZCA9IG1pbmlvCmF3c19zZWNyZXRfYWNjZXNzX2tleSA9IG1pbmlvMTIzCnMzX3BvcnQgPSA5MDAwCltzM10KaG9zdCA9IG1pbmlvLXN0b3JhZ2UuZHJhY29uLnN2YwpjYWxsaW5nX2Zvcm1hdCA9IGJvdG8uczMuY29ubmVjdGlvbi5PcmRpbmFyeUNhbGxpbmdGb3JtYXQKW0JvdG9dCmh0dHBzX3ZhbGlkYXRlX2NlcnRpZmljYXRlcyA9IEZhbHNlCmlzX3NlY3VyZSA9IEZhbHNlCltHU1V0aWxdCnByZWZlcl9hcGkgPSB4bWwK 67 | -------------------------------------------------------------------------------- /scripts/development/k8s/minio-storage/minio.conf: -------------------------------------------------------------------------------- 1 | [Credentials] 2 | aws_access_key_id = minio 3 | aws_secret_access_key = minio123 4 | s3_port = 9000 5 | [s3] 6 | host = minio-storage.dracon.svc 7 | calling_format = boto.s3.connection.OrdinaryCallingFormat 8 | [Boto] 9 | https_validate_certificates = False 10 | is_secure = False 11 | [GSUtil] 12 | prefer_api = xml 13 | -------------------------------------------------------------------------------- /scripts/development/k8s/namespace.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: dracon 6 | -------------------------------------------------------------------------------- /scripts/development/k8s/tektoncd-dashboard/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//build/defs:kustomize") 2 | 3 | kustomized_config( 4 | name = "tektoncd-dashboard", 5 | srcs = [ 6 | "ingress.yaml", 7 | "kustomization.yaml", 8 | "//third_party/k8s:tektoncd_dashboard", 9 | ], 10 | visibility = ["//scripts/development/..."], 11 | ) 12 | -------------------------------------------------------------------------------- /scripts/development/k8s/tektoncd-dashboard/ingress.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.k8s.io/v1beta1 3 | kind: Ingress 4 | metadata: 5 | name: tekton-dashboard 6 | namespace: tekton-pipelines 7 | annotations: 8 | kubernetes.io/ingress.class: nginx 9 | nginx.ingress.kubernetes.io/backend-protocol: "http" 10 | spec: 11 | backend: 12 | serviceName: tekton-dashboard 13 | servicePort: webinterface 14 | tls: 15 | - hosts: 16 | - "dashboard.localhost" 17 | secretName: tls-tekton-dashboard 18 | rules: 19 | - host: "dashboard.localhost" 20 | http: 21 | paths: 22 | - backend: 23 | serviceName: tekton-dashboard 24 | servicePort: http 25 | -------------------------------------------------------------------------------- /scripts/development/k8s/tektoncd-dashboard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | 5 | namespace: tekton-pipelines 6 | 7 | resources: 8 | - ingress.yaml 9 | - //third_party/k8s:tektoncd_dashboard 10 | -------------------------------------------------------------------------------- /scripts/development/kind/configuration.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Cluster 3 | apiVersion: kind.x-k8s.io/v1alpha4 4 | nodes: 5 | - role: control-plane 6 | image: ___KIND_IMAGE___ 7 | kubeadmConfigPatches: 8 | - | 9 | kind: InitConfiguration 10 | nodeRegistration: 11 | kubeletExtraArgs: 12 | node-labels: "ingress-ready=true" 13 | extraPortMappings: 14 | - containerPort: 80 15 | hostPort: 80 16 | protocol: TCP 17 | - containerPort: 443 18 | hostPort: 443 19 | protocol: TCP 20 | -------------------------------------------------------------------------------- /scripts/development/kind/dracon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script is a wrapper around dracon commands to give a 3 | # development experience as close to the release experience as possible. 4 | # e.g. a released command could be `dracon setup --namespace dracon --pipeline examples/pipelines/python-project` 5 | # in development, we provide the ability to run this prefixed with `plz`, 6 | # e.g. `plz dracon setup --namespace dracon --pipeline examples/pipelines/python-project` 7 | 8 | kubernetes_context=$(kubectl config current-context) 9 | if [ "${kubernetes_context}" != "kind-dracon" ]; then 10 | util::prompt "Are you sure you would like to run dracon against '${kubernetes_context}'?" 11 | fi 12 | 13 | kind_cluster_name="${kubernetes_context//kind-/}" 14 | 15 | args=() 16 | while [ $# -gt 0 ]; do 17 | case $1 in 18 | --pipeline) 19 | pipeline_target="//$2:dev" 20 | if [[ "${args[@]}" == *"run "* ]]; then 21 | util::load_development_images_into_kind "${kind_cluster_name}" "$pipeline_target" 22 | fi 23 | 24 | # convert pipeline flag to generated pipeline which has the local docker image tags 25 | ./pleasew build "$pipeline_target" 26 | pipeline_out_dir="$(dirname $(./pleasew query output $pipeline_target))" 27 | args+=("$1" "$pipeline_out_dir") 28 | shift 2 29 | ;; 30 | --*) 31 | args+=("$1" "$2") 32 | shift 2 33 | ;; 34 | *) 35 | args+=("$1") 36 | shift 1 37 | ;; 38 | esac 39 | done 40 | 41 | "$DRACON_BIN" "${args[@]}" 42 | -------------------------------------------------------------------------------- /scripts/development/kind/ingress/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "cert-manager", 3 | srcs = [ 4 | "cert-manager.yaml", 5 | ], 6 | visibility = ["//scripts/development/kind/..."], 7 | ) 8 | 9 | filegroup( 10 | name = "ingress-nginx", 11 | srcs = [ 12 | "ingress-nginx.yaml", 13 | ], 14 | visibility = ["//scripts/development/kind/..."], 15 | ) 16 | -------------------------------------------------------------------------------- /scripts/development/kind/ingress/cert-manager.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: cert-manager.io/v1alpha2 3 | kind: ClusterIssuer 4 | metadata: 5 | name: localhost-issuer 6 | spec: 7 | ca: 8 | secretName: localhost-issuer-ca-key 9 | -------------------------------------------------------------------------------- /scripts/development/kind/ingress/ingress-nginx.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: ingress-nginx 6 | -------------------------------------------------------------------------------- /scripts/development/kind/util.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | # util::load_development_images_into_kind builds and loads the given target's Docker images into the KinD cluster. 6 | util::load_development_images_into_kind() { 7 | local kind_cluster_name pipeline_target 8 | kind_cluster_name="$1" 9 | pipeline_target="$2" 10 | 11 | docker_fqn_targets=($(./pleasew query deps \ 12 | --include docker-fqn \ 13 | "$pipeline_target" \ 14 | | sort -u \ 15 | | awk '{ print $1 }' 16 | )) 17 | if [ "${#docker_fqn_targets[@]}" == 0 ]; then 18 | return 19 | fi 20 | util::rinfor "building ${#docker_fqn_targets[@]} image(s)" 21 | docker_load_targets=($(printf '%s\n' "${docker_fqn_targets[@]}" | sed 's/_fqn$/_load/g')) 22 | ./pleasew -p -v 2 --colour run parallel --output=quiet "${docker_load_targets[@]}" 23 | for docker_fqn_target in "${docker_fqn_targets[@]}"; do 24 | util::rinfor "loading $docker_fqn_target into kind cluster '$kind_cluster_name'" 25 | docker_fqn=$(<$(./pleasew query output $docker_fqn_target)) 26 | "$KIND_BIN" load docker-image \ 27 | "${docker_fqn}" \ 28 | --name "${kind_cluster_name}" > /dev/null 29 | done 30 | util::rsuccess "loaded development images in to kind cluster '$kind_cluster_name'" 31 | } 32 | -------------------------------------------------------------------------------- /scripts/fmt/BUILD: -------------------------------------------------------------------------------- 1 | sh_cmd( 2 | name = "go", 3 | srcs = ["go.sh"], 4 | cmd = """ 5 | source $(out_location //scripts/util) 6 | export GO_ROOT=$(out_location //third_party/lang:go_tool) 7 | export GO_FMT="\\\\$GO_ROOT/bin/gofmt" 8 | source $SRCS 9 | """, 10 | data = [ 11 | "//scripts/util", 12 | "//third_party/lang:go_tool", 13 | ], 14 | labels = ["fmt"], 15 | shell = "/bin/bash", 16 | ) 17 | 18 | sh_cmd( 19 | name = "plz", 20 | srcs = ["plz.sh"], 21 | cmd = """ 22 | source $(out_location //scripts/util) 23 | source $SRCS 24 | """, 25 | data = [ 26 | "//scripts/util", 27 | ], 28 | labels = ["fmt"], 29 | shell = "/bin/bash", 30 | ) 31 | -------------------------------------------------------------------------------- /scripts/fmt/go.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | util::infor "formatting go files" 5 | 6 | dirs=($(./pleasew query alltargets --include=go | grep -v third_party | cut -f1 -d":" | cut -c 3- | sort -u)) 7 | "${GO_FMT}" -s -w ${dirs[@]} 8 | 9 | util::rsuccess "formatted go files" 10 | -------------------------------------------------------------------------------- /scripts/fmt/plz.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | util::infor "formatting BUILD files" 5 | ./pleasew fmt --write 6 | util::rsuccess "formatted BUILD files" 7 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | if [[ "${OSTYPE}" == "linux-gnu" ]]; then 4 | arch="linux" 5 | else 6 | echo "-> unsupported OS: ${OSTYPE}" 7 | exit 1 8 | fi 9 | echo "-> determined ${arch} OS" 10 | 11 | install_path="${HOME}/.local/bin" 12 | if [ $(whoami) == "root" ]; then 13 | install_path="/usr/local/bin" 14 | fi 15 | echo "-> installing under ${install_path}" 16 | mkdir -p ${install_path} 17 | 18 | bin_path="${install_path}/dracon" 19 | repo="thought-machine/dracon" 20 | download_url=$(curl -s https://api.github.com/repos/${repo}/releases/latest \ 21 | | grep browser_download_url \ 22 | | cut -d '"' -f 4) 23 | echo "-> downloading ${download_url} to ${bin_path}" 24 | curl -L $download_url -o ${bin_path} 25 | chmod +x ${bin_path} 26 | 27 | echo "" 28 | echo "$PATH"|grep -q ${install_path} || echo "-> You need to add ${install_path} to your PATH variable. e.g. export PATH=${install_path}:\$PATH;" 29 | echo "" 30 | 31 | echo "$PATH"|grep -q ${install_path} || export PATH=${install_path}:$PATH 32 | dracon -h 33 | -------------------------------------------------------------------------------- /scripts/lint/BUILD: -------------------------------------------------------------------------------- 1 | sh_cmd( 2 | name = "go", 3 | srcs = ["go.sh"], 4 | cmd = """ 5 | source $(out_location //scripts/util) 6 | export GO_LINT=$(out_location //third_party/lang:golint) 7 | source $SRCS 8 | """, 9 | data = [ 10 | "//scripts/util", 11 | "//third_party/lang:golint", 12 | ], 13 | labels = ["lint"], # disable in `plz lint` until all are passing 14 | shell = "/bin/bash", 15 | ) 16 | 17 | sh_cmd( 18 | name = "plz", 19 | srcs = ["plz.sh"], 20 | cmd = """ 21 | source $(out_location //scripts/util) 22 | source $SRCS 23 | """, 24 | data = [ 25 | "//scripts/util", 26 | ], 27 | labels = ["lint"], 28 | shell = "/bin/bash", 29 | ) 30 | -------------------------------------------------------------------------------- /scripts/lint/go.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | util::infor "linting go files" 5 | 6 | dirs=($(./pleasew query alltargets --include=go | grep -v third_party | cut -f1 -d":" | cut -c 3- | sort -u)) 7 | if ! "${GO_LINT}" -set_exit_status ${dirs[@]}; then 8 | util::rerror "go files failed lint. To fix format errors, please run: 9 | $ ./pleasew run //scripts/fmt:go" 10 | exit 1 11 | fi 12 | 13 | util::rsuccess "linted go files" 14 | -------------------------------------------------------------------------------- /scripts/lint/plz.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | util::infor "checking BUILD files" 5 | if ! ./pleasew fmt --quiet; then 6 | util::rerror "BUILD files incorrectly formatted. Please run: 7 | $ ./pleasew run //scripts/fmt:plz" 8 | exit 1 9 | fi 10 | util::rsuccess "checked BUILD files" 11 | -------------------------------------------------------------------------------- /scripts/minikube.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | kubectl config use-context minikube 4 | 5 | # Tekton Pipelines 6 | kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/previous/v0.9.0/release.yaml 7 | 8 | # ES and Kibana 9 | minikube addons enable efk 10 | kubectl create ns dracon || true 11 | kubectl config set-context minikube --namespace=dracon 12 | 13 | # Enricher DB 14 | kubectl apply -f resources/persistence/enricher-db/k8s.yaml 15 | 16 | # Minio 17 | kubectl apply -f resources/persistence/minio-storage/k8s.yaml 18 | 19 | # Elasticsearch + Kibana 20 | kubectl apply -f resources/persistence/elasticsearch-kibana/elasticsearch.yaml 21 | kubectl apply -f resources/persistence/elasticsearch-kibana/kibana.yaml 22 | -------------------------------------------------------------------------------- /scripts/pre-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | git fetch --tags --force 5 | version=$(git describe --always) 6 | 7 | github_api_version="application/vnd.github.v3+json" 8 | github_owner="thought-machine" 9 | github_repo="dracon" 10 | 11 | post_release_json=$(cat < could not find release id in response" 34 | echo "${release_resp}" 35 | exit 1 36 | fi 37 | echo "-> created release ${release_id}" 38 | 39 | assets=$(find plz-out/bin/cmd/dracon -executable -type f \( ! -iname ".*" ! -iname "*_test" \)) 40 | for asset in ${assets}; do 41 | asset_name="${asset//plz-out\/bin\/cmd\//}" 42 | asset_name=$(dirname "${asset_name}") 43 | asset_name="${asset_name//\//_}" 44 | asset_resp=$(curl \ 45 | --header "Accept: ${github_api_version}" \ 46 | --header "Authorization: token ${GITHUB_TOKEN}" \ 47 | --header "Content-Type: application/octet-stream" \ 48 | --silent \ 49 | --request POST \ 50 | --data-binary @"${asset}" \ 51 | "https://uploads.github.com/repos/${github_owner}/${github_repo}/releases/${release_id}/assets?name=${asset_name}") 52 | asset_id=$(echo "${asset_resp}" | jq '.id') 53 | if [ -z "${asset_id}" ] || [ "${asset_id}" == "null" ]; then 54 | echo "!> could not find asset id in response" 55 | echo "${asset_resp}" 56 | exit 1 57 | fi 58 | echo "uploaded ${asset_name} to release ${release_id}" 59 | done 60 | 61 | echo "" 62 | echo "" 63 | echo "-> Created pre-release ${version} and uploaded assets" 64 | -------------------------------------------------------------------------------- /scripts/tag-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | git fetch origin master --tags &> /dev/null 5 | commit_sha=$(git rev-parse origin/master) 6 | 7 | highest_tag="$(git tag -l --sort=-v:refname | grep ^v | sed 1q)" 8 | 9 | major=$(echo "${highest_tag}" | cut -f1 -d. | tr -dc '0-9') 10 | minor=$(echo "${highest_tag}" | cut -f2 -d. | tr -dc '0-9') 11 | patch=$(echo "${highest_tag}" | cut -f3 -d. | tr -dc '0-9') 12 | 13 | printf "> The highest current tag is '%s' (major: %d, minor: %d, patch: %d)\n" "${highest_tag}" "${major}" "${minor}" "${patch}" 14 | printf "> What kind of release is this? (For guidance, see: https://semver.org/) [major/minor/patch] " 15 | read release_type 16 | case "${release_type}" in 17 | major) 18 | major=$((major+1)) 19 | minor=0 20 | patch=0 21 | ;; 22 | minor) 23 | minor=$((minor+1)) 24 | patch=0 25 | ;; 26 | patch) 27 | patch=$((patch+1)) 28 | ;; 29 | *) 30 | printf "!> Invalid option: '%s'.\n" "${release_type}" 31 | exit 1 32 | ;; 33 | esac 34 | 35 | new_tag="v${major}.${minor}.${patch}" 36 | 37 | printf "> The new release will be '%s'. Is this OK? [y/N] " "${new_tag}" 38 | read ok 39 | if [ "${ok}" != "y" ]; then 40 | printf "Not OK, exiting.\n" 41 | fi 42 | 43 | git tag --annotate "${new_tag}" --message "${new_tag}" "${commit_sha}" 44 | git push origin --tags 45 | -------------------------------------------------------------------------------- /scripts/util/BUILD: -------------------------------------------------------------------------------- 1 | sh_cmd( 2 | name = "util", 3 | srcs = ["util.sh"], 4 | cmd = """ 5 | source "$(out_location //third_party/sh:ansi)" 6 | YQ_BIN="$(out_location //third_party/tools:yq)" 7 | source $SRCS 8 | """, 9 | data = [ 10 | "//third_party/sh:ansi", 11 | "//third_party/tools:yq", 12 | ], 13 | visibility = [ 14 | "//scripts/...", 15 | "//test/...", 16 | ], 17 | ) 18 | -------------------------------------------------------------------------------- /source/git/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | docker_image( 4 | name = "dracon-source-git", 5 | srcs = ["git.sh"], 6 | image = "dracon-source-git", 7 | visibility = ["//examples/..."], 8 | ) 9 | -------------------------------------------------------------------------------- /source/git/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN apk add --update --no-cache git openssh-client tar 4 | 5 | COPY git.sh /git.sh 6 | -------------------------------------------------------------------------------- /source/git/git.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | git_src="/workspace/git-source" 4 | out_src="/workspace/output/source/source.tgz" 5 | 6 | cd "${git_src}" 7 | 8 | addr=$(git remote -v | cut -f1 -d" " | cut -f2 | head -n1 | cut -f2 -d"@") 9 | rev=$(git rev-parse HEAD) 10 | 11 | echo "${addr}?ref=${rev}" > .source.dracon 12 | 13 | tar -C "${git_src}/" -czf "${out_src}" . 14 | -------------------------------------------------------------------------------- /test/e2e/BUILD: -------------------------------------------------------------------------------- 1 | sh_cmd( 2 | name = "wait_for_pipelineruns", 3 | srcs = ["wait_for_pipelineruns.sh"], 4 | cmd = """ 5 | source "$(out_location //scripts/util)" 6 | source $SRCS 7 | """, 8 | data = [ 9 | "//scripts/util", 10 | ], 11 | shell = "/bin/bash", 12 | ) 13 | -------------------------------------------------------------------------------- /test/e2e/wait_for_pipelineruns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script waits for pipelineruns to finish and exits depending on if all pipelineruns were successful or not. 3 | set -euo pipefail 4 | 5 | 6 | function all_pipelineruns_succeeded { 7 | pipeline_runs=($(kubectl -n dracon get pipelineruns.tekton.dev -oyaml | \ 8 | $YQ_BIN e -N '.items[] | .metadata.name + "," + .status.conditions[0].reason' -)) 9 | has_pipeline_running=false 10 | has_pipeline_error=false 11 | 12 | for pipeline_run in "${pipeline_runs[@]}"; do 13 | name="$(echo "$pipeline_run" | cut -f1 -d,)" 14 | status="$(echo "$pipeline_run" | cut -f2 -d,)" 15 | 16 | case "$status" in 17 | "Running") 18 | util::rinfor "$name is Running..." 19 | has_pipeline_running=true 20 | ;; 21 | "Succeeded") 22 | ;; 23 | *) 24 | util::error "$name is $status" 25 | has_pipeline_error=true 26 | esac 27 | done 28 | 29 | if [ "$has_pipeline_error" = true ]; then 30 | return 1 31 | fi 32 | 33 | if [ "$has_pipeline_running" = true ]; then 34 | return 2 35 | fi 36 | 37 | return 0 38 | } 39 | 40 | time_limit_secs=3600 41 | intervals=100 42 | sleep_interval=$(($time_limit_secs/$intervals)) 43 | attempts=0 44 | 45 | util::info "waiting for all pipelines to succeed within ${time_limit_secs}s" 46 | 47 | set +x 48 | until all_pipelineruns_succeeded; do 49 | ec="$?" 50 | if [ "$ec" == 1 ]; then 51 | exit 1 52 | fi 53 | if [ $attempts -eq $intervals ]; then 54 | util::error "timed out" 55 | exit 1 56 | fi 57 | attempts=$((attempts + 1)) 58 | sleep $sleep_interval 59 | done 60 | set -e 61 | 62 | util::rsuccess "all pipelineruns completed successfully" 63 | -------------------------------------------------------------------------------- /third_party/defs/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["PUBLIC"]) 2 | 3 | remote_file( 4 | name = "docker", 5 | hashes = ["9c0a6d6312dc36e23d8738d2d15d0dfa06cb996ab7f45e0d124cb6d65fbdb40f"], 6 | url = "https://raw.githubusercontent.com/thought-machine/pleasings/7fa34bc749ff31008680f5d113ad958a73d84e07/docker/docker.build_defs", 7 | ) 8 | 9 | remote_file( 10 | name = "go_mock", 11 | hashes = ["0e0a9eb993b9ccafce1b9b4bb8d376f4c4ff8c97ac2a12d5b0d4a6d21c1255de"], 12 | url = "https://raw.githubusercontent.com/thought-machine/pleasings/master/go/go_mock.build_defs", 13 | ) 14 | -------------------------------------------------------------------------------- /third_party/k8s/BUILD: -------------------------------------------------------------------------------- 1 | KUBERNETES_INGRESSNGINX_VERSION = "3.25.0" 2 | 3 | remote_file( 4 | name = "kubernetes_ingressnginx", 5 | hashes = ["5cea6ce0f7e285f88847852aab05902406a63bfe4aef297831ea0207111fae44"], 6 | url = f"https://raw.githubusercontent.com/kubernetes/ingress-nginx/helm-chart-{KUBERNETES_INGRESSNGINX_VERSION}/deploy/static/provider/kind/deploy.yaml", 7 | visibility = ["//scripts/development/kind/..."], 8 | ) 9 | 10 | JETSTACK_CERTMANAGER_VERSION = "1.1.0" 11 | 12 | remote_file( 13 | name = "jetstack_certmanager", 14 | hashes = ["651857a32b0be92f3c5c274729eea9c34bd457726bd3f560b3f68399659d1cfd"], 15 | url = f"https://github.com/jetstack/cert-manager/releases/download/v{JETSTACK_CERTMANAGER_VERSION}/cert-manager.yaml", 16 | visibility = ["//scripts/development/kind/..."], 17 | ) 18 | 19 | TEKTONCD_PIPELINE_VERSION = "0.34.1" 20 | 21 | remote_file( 22 | name = "tektoncd_pipeline", 23 | hashes = ["9b12e7ed10e38aba742c22b49148fdccf806a985bf94de5c4c9857dd31e5ba75"], 24 | url = f"https://github.com/tektoncd/pipeline/releases/download/v{TEKTONCD_PIPELINE_VERSION}/release.yaml", 25 | visibility = ["//scripts/development/..."], 26 | ) 27 | 28 | TEKTONCD_DASHBOARD_VERSION = "0.25.0" 29 | 30 | remote_file( 31 | name = "tektoncd_dashboard", 32 | hashes = ["955a4e3afbdbaa3b67577154d5bf022888629ff3ea70d99197c0c1c6b529e056"], 33 | url = f"https://github.com/tektoncd/dashboard/releases/download/v{TEKTONCD_DASHBOARD_VERSION}/tekton-dashboard-release-readonly.yaml", 34 | visibility = ["//scripts/development/..."], 35 | ) 36 | -------------------------------------------------------------------------------- /third_party/lang/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["PUBLIC"]) 2 | 3 | go_toolchain( 4 | name = "go_tool", 5 | hashes = [ 6 | "acc512fbab4f716a8f97a8b3fbaa9ddd39606a28be6c2515ef7c6c6311acffde", # linux-amd64 7 | ], 8 | version = "1.19.1", 9 | ) 10 | 11 | go_module( 12 | name = "golint", 13 | binary = True, 14 | hashes = [ 15 | "6a2982682499be8804589a0082514d650ca4019a88daeed951a215d56ff4f8b8", 16 | ], 17 | install = ["golint"], 18 | module = "golang.org/x/lint", 19 | version = "83fdc39ff7b56453e3793356bcff3070b9b96445", 20 | deps = [ 21 | "//third_party/go:x_tools", 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /third_party/proto/BUILD: -------------------------------------------------------------------------------- 1 | version = "3.6.0" 2 | 3 | hash = "828e97c42aa9209f6e025e55963605e178eb1e3fe95d35d48141eddd3f9235cf" 4 | 5 | remote_file( 6 | name = "protoc_zip", 7 | out = "protoc-%s.zip" % version, 8 | hashes = [ 9 | hash, 10 | ], 11 | url = "https://github.com/google/protobuf/releases/download/v%s/protoc-%s-$XOS-$XARCH.zip" % (version, version), 12 | ) 13 | 14 | genrule( 15 | name = "protoc", 16 | srcs = [":protoc_zip"], 17 | outs = ["protoc"], 18 | binary = True, 19 | cmd = "$TOOL x $SRCS bin/protoc", 20 | tools = [CONFIG.JARCAT_TOOL], 21 | visibility = ["PUBLIC"], 22 | deps = [":protoc_deps"], 23 | ) 24 | 25 | genrule( 26 | name = "protoc_deps", 27 | srcs = [":protoc_zip"], 28 | outs = ["include"], 29 | binary = True, 30 | cmd = "$TOOL x $SRCS", 31 | tools = [CONFIG.JARCAT_TOOL], 32 | visibility = ["PUBLIC"], 33 | ) 34 | -------------------------------------------------------------------------------- /third_party/python/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["PUBLIC"]) 2 | 3 | pip_library( 4 | name = "grpc", 5 | package_name = "grpcio", 6 | hashes = [ 7 | "6b6839032627170b468ca08e2d75d78a66cd4b9c83af0742f6d4c46f3a877d52", # TM Mirror 8 | "d1660bcf7d90b1ebe054f2e1a66ce0d96c80f5b5d0ccb3e52ee80649a938a944", # Public 9 | "f2a850039cbe56880e99a44015ab1cba050ec3370192494047d1931b7572591e", # GitHub Actions 10 | ], 11 | licences = ["Apache 2.0"], 12 | version = "1.38.0", 13 | ) 14 | 15 | pip_library( 16 | name = "protobuf", 17 | licences = ["BSD 3-Clause"], 18 | version = "3.11.3", 19 | deps = [":six"], 20 | ) 21 | 22 | pip_library( 23 | name = "pkg_resources", 24 | package_name = "setuptools", 25 | hashes = [ 26 | "57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54", # TM Mirror 27 | "a951e6c5a85abc380f46c0cd8780a1a03581e82edd08b64defebe8f52ae6ab8d", # Public 28 | "c4a1ddda2582eb4c681ab9fc6d7e6ac5a0d34999d335623ec282a062ea384060", # GitHub Actions 29 | ], 30 | licences = ["MIT"], 31 | version = "65.6.3", 32 | ) 33 | 34 | pip_library( 35 | name = "six", 36 | package_name = "six", 37 | licences = ["MIT"], 38 | version = "1.16.0", 39 | ) 40 | 41 | pip_library( 42 | name = "requests", 43 | version = "2.23.0", 44 | deps = [ 45 | ":certifi", 46 | ":chardet", 47 | ":idna", 48 | ":urllib3", 49 | ], 50 | ) 51 | 52 | pip_library( 53 | name = "urllib3", 54 | version = "1.25.8", 55 | ) 56 | 57 | pip_library( 58 | name = "certifi", 59 | version = "2019.11.28", 60 | ) 61 | 62 | pip_library( 63 | name = "chardet", 64 | version = "3.0.4", 65 | ) 66 | 67 | pip_library( 68 | name = "idna", 69 | version = "2.9", 70 | ) 71 | 72 | pip_library( 73 | name = "defectdojo_api", 74 | licences = ["MIT"], 75 | version = "1.1.3", 76 | deps = [":requests"], 77 | ) 78 | -------------------------------------------------------------------------------- /third_party/sh/BUILD: -------------------------------------------------------------------------------- 1 | remote_file( 2 | name = "shflags", 3 | hashes = ["efe6952ff6dd576325ab42c8c1e23c8e0ce6192ce4c4fd992cd9e6058a09e44e"], 4 | licences = ["Apache-2.0"], 5 | url = f"https://raw.githubusercontent.com/kward/shflags/46e081ec6e08313e4f0f314feee6650fe44eabfc/shflags", 6 | visibility = ["//scripts/..."], 7 | ) 8 | 9 | remote_file( 10 | name = "ansi", 11 | hashes = ["8c290aaa0c7b38f26b49cdaef32b5dd9991f7dd8ce4c11679d2f70885be16bb6"], 12 | licences = ["MIT"], 13 | url = "https://raw.githubusercontent.com/fidian/ansi/225a28e589a93f78820dbf921c8261aeea964676/ansi", 14 | visibility = ["//scripts/..."], 15 | ) 16 | -------------------------------------------------------------------------------- /third_party/tools/BUILD: -------------------------------------------------------------------------------- 1 | KIND_VERSION = "0.12.0" 2 | 3 | remote_file( 4 | name = "kind", 5 | binary = True, 6 | hashes = [ 7 | "b80624c14c807490c0944d21fdc9c3455d6cc904fad486fe236f2187ecaa5789", # linux-amd64 8 | ], 9 | url = f"https://github.com/kubernetes-sigs/kind/releases/download/v{KIND_VERSION}/kind-{CONFIG.OS}-{CONFIG.ARCH}", 10 | visibility = ["//scripts/..."], 11 | ) 12 | 13 | remote_file( 14 | name = "yq", 15 | binary = True, 16 | extract = True, 17 | hashes = [ 18 | "0e105edbb0ebc7c4115c610168f1d6b0ff3ceb38043dac92e18fa0698130d69f", # linux_amd64 19 | ], 20 | url = f"https://github.com/mikefarah/yq/releases/download/v4.6.3/yq_{CONFIG.OS}_{CONFIG.ARCH}.tar.gz", 21 | visibility = ["//scripts/..."], 22 | ) 23 | 24 | KUSTOMIZE_VERSION = "v3.8.7" 25 | 26 | remote_file( 27 | name = "kustomize", 28 | binary = True, 29 | extract = True, 30 | hashes = [ 31 | "4a3372d7bfdffe2eaf729e77f88bc94ce37dc84de55616bfe90aac089bf6fd02", # linux-amd64 32 | ], 33 | url = f"https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2F{KUSTOMIZE_VERSION}/kustomize_{KUSTOMIZE_VERSION}_{CONFIG.OS}_{CONFIG.ARCH}.tar.gz", 34 | visibility = [ 35 | "//examples/...", 36 | "//scripts/development/k8s/...", 37 | ], 38 | ) 39 | -------------------------------------------------------------------------------- /tools/npm_audit/BUILD: -------------------------------------------------------------------------------- 1 | subinclude("//third_party/defs:docker") 2 | 3 | filegroup( 4 | name = "npm_audit", 5 | srcs = [ 6 | "index.js", 7 | "package.json", 8 | "package-lock.json", 9 | ], 10 | ) 11 | 12 | docker_image( 13 | name = "npm-audit", 14 | srcs = [ 15 | ":npm_audit", 16 | ], 17 | dockerfile = "Dockerfile-tool-npm-audit", 18 | image = "dracon-tool-npm-audit", 19 | ) 20 | -------------------------------------------------------------------------------- /tools/npm_audit/Dockerfile-tool-npm-audit: -------------------------------------------------------------------------------- 1 | FROM node:15-alpine3.12 as node 2 | 3 | RUN mkdir -p /npm-audit 4 | 5 | COPY /index.js /npm-audit/ 6 | COPY /package.json /npm-audit/ 7 | COPY /package-lock.json /npm-audit/ 8 | 9 | RUN apk add -U --no-cache ca-certificates \ 10 | && cd /npm-audit \ 11 | && npm install --production \ 12 | && rm -rf /tmp/v8-compile-cache-* 13 | 14 | WORKDIR / 15 | ENTRYPOINT ["/usr/local/bin/node", "/npm-audit/index.js"] 16 | -------------------------------------------------------------------------------- /tools/npm_audit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "npm_audit", 3 | "version": "0.1.0", 4 | "description": "A Dracon tool container for 'npm audit' and 'yarn audit'", 5 | "main": "index.js", 6 | "scripts": {}, 7 | "author": "Chris Novakovic", 8 | "license": "ISC", 9 | "dependencies": { 10 | "await-spawn": "^4.0.1", 11 | "filehound": "^1.17.4" 12 | } 13 | } 14 | --------------------------------------------------------------------------------