diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e29ca3aff9..31855221d4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -290,6 +290,7 @@ jobs: - nmap - nuclei - screenshooter + - semgrep - ssh-scan - sslyze - trivy diff --git a/.github/workflows/scb-bot.yaml b/.github/workflows/scb-bot.yaml index 2144f362a7..5b8b62a7a9 100644 --- a/.github/workflows/scb-bot.yaml +++ b/.github/workflows/scb-bot.yaml @@ -23,6 +23,7 @@ jobs: # - wpscan # - zap # - zap-advanced + # - semgrep # These are commented out for the moment to avoid accidental multiple erroneous PRs # missing scanners are : nmap, nikto, typo3scan steps: diff --git a/hooks/persistence-defectdojo/.helm-docs.gotmpl b/hooks/persistence-defectdojo/.helm-docs.gotmpl index 79e0f954dc..a83c226804 100644 --- a/hooks/persistence-defectdojo/.helm-docs.gotmpl +++ b/hooks/persistence-defectdojo/.helm-docs.gotmpl @@ -35,6 +35,7 @@ These are: - SSLyze - Trivy - Gitleaks +- Semgrep After uploading the results to DefectDojo, it will use the findings parsed by DefectDojo to overwrite the original secureCodeBox findings identified by the parser. This lets you access the finding metadata like the false diff --git a/hooks/persistence-defectdojo/README.md b/hooks/persistence-defectdojo/README.md index ac160dca36..550c6f3792 100644 --- a/hooks/persistence-defectdojo/README.md +++ b/hooks/persistence-defectdojo/README.md @@ -46,6 +46,7 @@ These are: - SSLyze - Trivy - Gitleaks +- Semgrep After uploading the results to DefectDojo, it will use the findings parsed by DefectDojo to overwrite the original secureCodeBox findings identified by the parser. This lets you access the finding metadata like the false diff --git a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/util/ScanNameMapping.java b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/util/ScanNameMapping.java index b6d97219b8..fe997092ac 100644 --- a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/util/ScanNameMapping.java +++ b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/util/ScanNameMapping.java @@ -18,6 +18,7 @@ public enum ScanNameMapping { NIKTO("nikto", ScanType.NIKTO_SCAN), NUCLEI("nuclei", ScanType.NUCLEI_SCAN), WPSCAN("wpscan", ScanType.WPSCAN), + SEMGREP("semgrep", ScanType.SEMGREP_JSON_REPORT), GENERIC(null, ScanType.GENERIC_FINDINGS_IMPORT) ; diff --git a/scanners/semgrep/.helm-docs.gotmpl b/scanners/semgrep/.helm-docs.gotmpl new file mode 100644 index 0000000000..1987de9407 --- /dev/null +++ b/scanners/semgrep/.helm-docs.gotmpl @@ -0,0 +1,158 @@ +{{- /* +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 +*/ -}} + +{{- define "extra.docsSection" -}} +--- +title: "Semgrep" +category: "scanner" +type: "Repository" +state: "released" +appVersion: "{{ template "chart.appVersion" . }}" +usecase: "Static Code Analysis" +--- + +![Semgrep logo](https://raw.githubusercontent.com/returntocorp/semgrep-docs/main/static/img/semgrep-icon-text-horizontal.svg) + +{{- end }} + +{{- define "extra.dockerDeploymentSection" -}} +## Supported Tags +- `latest` (represents the latest stable release build) +- tagged releases, e.g. `{{ template "chart.appVersion" . }}` +{{- end }} + +{{- define "extra.chartAboutSection" -}} +## What is Semgrep? +Semgrep ("semantic grep") is a static source code analyzer that can be used to search for specific patterns in code. +It allows you to either [write your own rules](https://semgrep.dev/learn), or use one of the [many pre-defined rulesets](https://semgrep.dev/r) curated by the semgrep team. + +To learn more about semgrep, visit [semgrep.dev](https://semgrep.dev). + +{{- end }} + +{{- define "extra.scannerConfigurationSection" -}} +## Scanner Configuration + +Semgrep requires one or more ruleset(s) to run its scans. +Refer to the [semgrep rule database](https://semgrep.dev/r) for more details. +A good starting point would be [p/ci](https://semgrep.dev/p/ci) (for security checks with a low false-positive rate) or [p/security-audit](https://semgrep.dev/p/security-audit) (for a more comprehensive security audit, which may include more false-positive results). + + +Semgrep needs access to the source code to run its analysis. +To use it with secureCodeBox, you thus need a way to provision the data into the scan container. +The recommended method is to use `initContainers` to clone a VCS repository. +The simplest example, using a public Git repository from GitHub, looks like this: + +```yaml +apiVersion: "execution.securecodebox.io/v1" +kind: Scan +metadata: + name: "semgrep-vulnerable-flask-app" +spec: + # Specify a Kubernetes volume that will be shared between the scanner and the initContainer + volumes: + - name: repository + emptyDir: {} + # Mount the volume in the scan container + volumeMounts: + - mountPath: "/repo/" + name: repository + # Specify an init container to clone the repository + initContainers: + - name: "provision-git" + # Use an image that includes git + image: bitnami/git + # Mount the same volume we also use in the main container + volumeMounts: + - mountPath: "/repo/" + name: repository + # Specify the clone command and clone into the volume, mounted at /repo/ + command: + - git + - clone + - "https://github.com/we45/Vulnerable-Flask-App" + - /repo/flask-app + # Parameterize the semgrep scan itself + scanType: "semgrep" + parameters: + - "-c" + - "p/ci" + - "/repo/flask-app" +``` + +If your repository requires authentication to clone, you will have to give the initContainer access to some method of authentication. +This could be a personal access token ([GitHub](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token), [GitLab](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)), project access token ([GitLab](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html)), deploy key ([GitHub](https://docs.github.com/en/developers/overview/managing-deploy-keys#deploy-keys) / [GitLab](https://docs.gitlab.com/ee/user/project/deploy_keys/)), deploy token ([GitLab](https://docs.gitlab.com/ee/user/project/deploy_tokens/)), or a server-to-server token ([GitHub](https://docs.github.com/en/developers/overview/managing-deploy-keys#server-to-server-tokens)). +Due to the large variety of options, we do not provide documentation for all of them here. +Refer to the linked documentation for details on the different methods, and remember to use [Kubernetes secrets](https://kubernetes.io/docs/concepts/configuration/secret/) to manage keys and tokens. + +## Cascading Rules +By default, the semgrep scanner does not install any [cascading rules](docs/hooks/cascading-scans), as some aspects of the semgrep scan (like the used ruleset) should be customized. +However, you can easily create your own cascading rule, for example to run semgrep on the output of [git-repo-scanner](docs/scanners/git-repo-scanner). +As a starting point, consider the following cascading rule to scan all public GitHub repositories found by git-repo-scanner using the p/ci ruleset of semgrep: + +```yaml +apiVersion: "cascading.securecodebox.io/v1" +kind: CascadingRule +metadata: + name: "semgrep-public-github-repos" + labels: + securecodebox.io/invasive: non-invasive + securecodebox.io/intensive: medium +spec: + matches: + anyOf: + # We want to scan public GitHub repositories. Change "public" to "private" to scan private repos instead + - name: "GitHub Repo" + attributes: + visibility: public + scanSpec: + # Configure the scanSpec for semgrep + scanType: "semgrep" + parameters: + - "-c" + - "p/ci" # Change this to use a different rule set + - "/repo/" + volumes: + - name: repo + emptyDir: {} + volumeMounts: + - name: repo + mountPath: "/repo/" + initContainers: + - name: "git-clone" + image: bitnami/git + # The command assumes that GITHUB_TOKEN contains a GitHub access token with access to the repository. + # GITHUB_TOKEN is set below in the "env" section. + # If you do not wan to use an access token, remove it from the URL below. + command: + - git + - clone + - "https://$(GITHUB_TOKEN)@github.com/{{ "{{{" }}attributes.full_name{{ "}}}" }}" + - /repo/ + volumeMounts: + - mountPath: "/repo/" + name: repo + # Load the GITHUB_TOKEN from the kubernetes secret with the name "github-access-token" + # Create this secret using, for example: + # echo -n 'YOUR TOKEN GOES HERE' > github-token.txt && kubectl create secret generic github-access-token --from-file=token=github-token.txt + # IMPORTANT: Ensure that github-token.txt does not have a new line at the end of the file. This is automatically done by using "echo -n" to create it. + # However, if you create it with an editor, some editors (most notably, vim) will create hidden newlines at the end of files, which will cause issues. + env: + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: github-access-token + key: token +``` + +Use this configuration as a baseline for your own rules. +{{- end }} + +{{- define "extra.chartConfigurationSection" -}} +{{- end }} + +{{- define "extra.scannerLinksSection" -}} +{{- end }} diff --git a/scanners/semgrep/.helmignore b/scanners/semgrep/.helmignore new file mode 100644 index 0000000000..fb9c1d2d6f --- /dev/null +++ b/scanners/semgrep/.helmignore @@ -0,0 +1,40 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ +# Node.js files +node_modules/* +package.json +package-lock.json +src/* +config/* +Dockerfile +.dockerignore +*.tar +parser/* +scanner/* +integration-tests/* +examples/* +docs/* +Makefile diff --git a/scanners/semgrep/Chart.yaml b/scanners/semgrep/Chart.yaml new file mode 100644 index 0000000000..c93e694fcf --- /dev/null +++ b/scanners/semgrep/Chart.yaml @@ -0,0 +1,45 @@ +apiVersion: v2 +name: semgrep +description: A Helm chart for the semgrep semantic code analyzer that integrates with the secureCodeBox + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: "v3.1.0-alpha1" + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.70.0" + +versionApi: https://api.github.com/repos/returntocorp/semgrep/releases/latest + +kubeVersion: ">=v1.11.0-0" + +home: https://docs.securecodebox.io/docs/scanners/semgrep +icon: https://docs.securecodebox.io/img/integrationIcons/semgrep.svg # TODO: Add this + +sources: + - https://github.com/secureCodeBox/secureCodeBox + +maintainers: + - name: iteratec GmbH + - email: secureCodeBox@iteratec.com + +keywords: + - security + - semgrep + - SAST + - staticanalysis + - secureCodeBox \ No newline at end of file diff --git a/scanners/semgrep/Makefile b/scanners/semgrep/Makefile new file mode 100644 index 0000000000..e5cc76154a --- /dev/null +++ b/scanners/semgrep/Makefile @@ -0,0 +1,15 @@ +#!/usr/bin/make -f + +include_guard = set # Always include this line (checked in the makefile framework) +scanner = semgrep + +include ../../scanners.mk # Ensures that all the default makefile targets are included + +integration-tests: + @echo ".: 🩺 Starting integration test in kind namespace 'integration-tests'." + kubectl -n integration-tests delete scans --all + cd ../../tests/integration/ && npm ci + cd ../../scanners/${scanner} + kubectl -n integration-tests create configmap semgrep-test-file --from-file=integration-tests/testfile.py + npx --yes --package jest@$(JEST_VERSION) jest --verbose --ci --colors --coverage --passWithNoTests ${scanner}/integration-tests + kubectl -n integration-tests delete configmap semgrep-test-file diff --git a/scanners/semgrep/README.md b/scanners/semgrep/README.md new file mode 100644 index 0000000000..edcbcb31b6 --- /dev/null +++ b/scanners/semgrep/README.md @@ -0,0 +1,209 @@ +--- +title: "Semgrep" +category: "scanner" +type: "Repository" +state: "released" +appVersion: "0.70.0" +usecase: "Static Code Analysis" +--- + +![Semgrep logo](https://raw.githubusercontent.com/returntocorp/semgrep-docs/main/static/img/semgrep-icon-text-horizontal.svg) + + + + +

+ License Apache-2.0 + GitHub release (latest SemVer) + OWASP Incubator Project + Artifact HUB + GitHub Repo stars + Twitter Follower +

+ +## What is Semgrep? +Semgrep ("semantic grep") is a static source code analyzer that can be used to search for specific patterns in code. +It allows you to either [write your own rules](https://semgrep.dev/learn), or use one of the [many pre-defined rulesets](https://semgrep.dev/r) curated by the semgrep team. + +To learn more about semgrep, visit [semgrep.dev](https://semgrep.dev). + +## Deployment +The semgrep chart can be deployed via helm: + +```bash +# Install HelmChart (use -n to configure another namespace) +helm upgrade --install semgrep secureCodeBox/semgrep +``` + +## Scanner Configuration + +Semgrep requires one or more ruleset(s) to run its scans. +Refer to the [semgrep rule database](https://semgrep.dev/r) for more details. +A good starting point would be [p/ci](https://semgrep.dev/p/ci) (for security checks with a low false-positive rate) or [p/security-audit](https://semgrep.dev/p/security-audit) (for a more comprehensive security audit, which may include more false-positive results). + +Semgrep needs access to the source code to run its analysis. +To use it with secureCodeBox, you thus need a way to provision the data into the scan container. +The recommended method is to use `initContainers` to clone a VCS repository. +The simplest example, using a public Git repository from GitHub, looks like this: + +```yaml +apiVersion: "execution.securecodebox.io/v1" +kind: Scan +metadata: + name: "semgrep-vulnerable-flask-app" +spec: + # Specify a Kubernetes volume that will be shared between the scanner and the initContainer + volumes: + - name: repository + emptyDir: {} + # Mount the volume in the scan container + volumeMounts: + - mountPath: "/repo/" + name: repository + # Specify an init container to clone the repository + initContainers: + - name: "provision-git" + # Use an image that includes git + image: bitnami/git + # Mount the same volume we also use in the main container + volumeMounts: + - mountPath: "/repo/" + name: repository + # Specify the clone command and clone into the volume, mounted at /repo/ + command: + - git + - clone + - "https://github.com/we45/Vulnerable-Flask-App" + - /repo/flask-app + # Parameterize the semgrep scan itself + scanType: "semgrep" + parameters: + - "-c" + - "p/ci" + - "/repo/flask-app" +``` + +If your repository requires authentication to clone, you will have to give the initContainer access to some method of authentication. +This could be a personal access token ([GitHub](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token), [GitLab](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)), project access token ([GitLab](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html)), deploy key ([GitHub](https://docs.github.com/en/developers/overview/managing-deploy-keys#deploy-keys) / [GitLab](https://docs.gitlab.com/ee/user/project/deploy_keys/)), deploy token ([GitLab](https://docs.gitlab.com/ee/user/project/deploy_tokens/)), or a server-to-server token ([GitHub](https://docs.github.com/en/developers/overview/managing-deploy-keys#server-to-server-tokens)). +Due to the large variety of options, we do not provide documentation for all of them here. +Refer to the linked documentation for details on the different methods, and remember to use [Kubernetes secrets](https://kubernetes.io/docs/concepts/configuration/secret/) to manage keys and tokens. + +## Cascading Rules +By default, the semgrep scanner does not install any [cascading rules](docs/hooks/cascading-scans), as some aspects of the semgrep scan (like the used ruleset) should be customized. +However, you can easily create your own cascading rule, for example to run semgrep on the output of [git-repo-scanner](docs/scanners/git-repo-scanner). +As a starting point, consider the following cascading rule to scan all public GitHub repositories found by git-repo-scanner using the p/ci ruleset of semgrep: + +```yaml +apiVersion: "cascading.securecodebox.io/v1" +kind: CascadingRule +metadata: + name: "semgrep-public-github-repos" + labels: + securecodebox.io/invasive: non-invasive + securecodebox.io/intensive: medium +spec: + matches: + anyOf: + # We want to scan public GitHub repositories. Change "public" to "private" to scan private repos instead + - name: "GitHub Repo" + attributes: + visibility: public + scanSpec: + # Configure the scanSpec for semgrep + scanType: "semgrep" + parameters: + - "-c" + - "p/ci" # Change this to use a different rule set + - "/repo/" + volumes: + - name: repo + emptyDir: {} + volumeMounts: + - name: repo + mountPath: "/repo/" + initContainers: + - name: "git-clone" + image: bitnami/git + # The command assumes that GITHUB_TOKEN contains a GitHub access token with access to the repository. + # GITHUB_TOKEN is set below in the "env" section. + # If you do not wan to use an access token, remove it from the URL below. + command: + - git + - clone + - "https://$(GITHUB_TOKEN)@github.com/{{{attributes.full_name}}}" + - /repo/ + volumeMounts: + - mountPath: "/repo/" + name: repo + # Load the GITHUB_TOKEN from the kubernetes secret with the name "github-access-token" + # Create this secret using, for example: + # echo -n 'YOUR TOKEN GOES HERE' > github-token.txt && kubectl create secret generic github-access-token --from-file=token=github-token.txt + # IMPORTANT: Ensure that github-token.txt does not have a new line at the end of the file. This is automatically done by using "echo -n" to create it. + # However, if you create it with an editor, some editors (most notably, vim) will create hidden newlines at the end of files, which will cause issues. + env: + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: github-access-token + key: token +``` + +Use this configuration as a baseline for your own rules. + +## Requirements + +Kubernetes: `>=v1.11.0-0` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| cascadingRules.enabled | bool | `true` | Enables or disables the installation of the default cascading rules for this scanner | +| parser.backoffLimit | int | `3` | | +| parser.env | list | `[]` | | +| parser.image.pullPolicy | string | `"IfNotPresent"` | | +| parser.image.repository | string | `"securecodebox/parser-semgrep"` | | +| parser.image.tag | string | `nil` | | +| scanner.backoffLimit | int | `3` | | +| scanner.env | list | `[]` | | +| scanner.extraContainers | list | `[]` | | +| scanner.extraVolumeMounts | list | `[]` | | +| scanner.extraVolumes | list | `[]` | | +| scanner.image.pullPolicy | string | `"IfNotPresent"` | | +| scanner.image.repository | string | `"docker.io/returntocorp/semgrep"` | | +| scanner.image.tag | string | `nil` | | +| scanner.resources | object | `{}` | | +| scanner.securityContext.allowPrivilegeEscalation | bool | `false` | | +| scanner.securityContext.capabilities.drop[0] | string | `"all"` | | +| scanner.securityContext.privileged | bool | `false` | | +| scanner.securityContext.readOnlyRootFilesystem | bool | `false` | | +| scanner.securityContext.runAsNonRoot | bool | `true` | | +| scanner.ttlSecondsAfterFinished | string | `nil` | | + +## License +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +Code of secureCodeBox is licensed under the [Apache License 2.0][scb-license]. + +[scb-owasp]: https://www.owasp.org/index.php/OWASP_secureCodeBox +[scb-docs]: https://docs.securecodebox.io/ +[scb-site]: https://www.securecodebox.io/ +[scb-github]: https://github.com/secureCodeBox/ +[scb-twitter]: https://twitter.com/secureCodeBox +[scb-slack]: https://join.slack.com/t/securecodebox/shared_invite/enQtNDU3MTUyOTM0NTMwLTBjOWRjNjVkNGEyMjQ0ZGMyNDdlYTQxYWQ4MzNiNGY3MDMxNThkZjJmMzY2NDRhMTk3ZWM3OWFkYmY1YzUxNTU +[scb-license]: https://github.com/secureCodeBox/secureCodeBox/blob/master/LICENSE + diff --git a/scanners/semgrep/docs/.gitkeep b/scanners/semgrep/docs/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scanners/semgrep/docs/README.ArtifactHub.md b/scanners/semgrep/docs/README.ArtifactHub.md new file mode 100644 index 0000000000..0c267fd99e --- /dev/null +++ b/scanners/semgrep/docs/README.ArtifactHub.md @@ -0,0 +1,229 @@ + + + +

+ License Apache-2.0 + GitHub release (latest SemVer) + OWASP Incubator Project + Artifact HUB + GitHub Repo stars + Twitter Follower +

+ +## What is OWASP secureCodeBox? + +

+ secureCodeBox Logo +

+ +_[OWASP secureCodeBox][scb-github]_ is an automated and scalable open source solution that can be used to integrate various *security vulnerability scanners* with a simple and lightweight interface. The _secureCodeBox_ mission is to support *DevSecOps* Teams to make it easy to automate security vulnerability testing in different scenarios. + +With the _secureCodeBox_ we provide a toolchain for continuous scanning of applications to find the low-hanging fruit issues early in the development process and free the resources of the penetration tester to concentrate on the major security issues. + +The secureCodeBox project is running on [Kubernetes](https://kubernetes.io/). To install it you need [Helm](https://helm.sh), a package manager for Kubernetes. It is also possible to start the different integrated security vulnerability scanners based on a docker infrastructure. + +### Quickstart with secureCodeBox on kubernetes + +You can find resources to help you get started on our [documentation website](https://docs.securecodebox.io) including instruction on how to [install the secureCodeBox project](https://docs.securecodebox.io/docs/getting-started/installation) and guides to help you [run your first scans](https://docs.securecodebox.io/docs/getting-started/first-scans) with it. + +## What is Semgrep? +Semgrep ("semantic grep") is a static source code analyzer that can be used to search for specific patterns in code. +It allows you to either [write your own rules](https://semgrep.dev/learn), or use one of the [many pre-defined rulesets](https://semgrep.dev/r) curated by the semgrep team. + +To learn more about semgrep, visit [semgrep.dev](https://semgrep.dev). + +## Deployment +The semgrep chart can be deployed via helm: + +```bash +# Install HelmChart (use -n to configure another namespace) +helm upgrade --install semgrep secureCodeBox/semgrep +``` + +## Scanner Configuration + +Semgrep requires one or more ruleset(s) to run its scans. +Refer to the [semgrep rule database](https://semgrep.dev/r) for more details. +A good starting point would be [p/ci](https://semgrep.dev/p/ci) (for security checks with a low false-positive rate) or [p/security-audit](https://semgrep.dev/p/security-audit) (for a more comprehensive security audit, which may include more false-positive results). + +Semgrep needs access to the source code to run its analysis. +To use it with secureCodeBox, you thus need a way to provision the data into the scan container. +The recommended method is to use `initContainers` to clone a VCS repository. +The simplest example, using a public Git repository from GitHub, looks like this: + +```yaml +apiVersion: "execution.securecodebox.io/v1" +kind: Scan +metadata: + name: "semgrep-vulnerable-flask-app" +spec: + # Specify a Kubernetes volume that will be shared between the scanner and the initContainer + volumes: + - name: repository + emptyDir: {} + # Mount the volume in the scan container + volumeMounts: + - mountPath: "/repo/" + name: repository + # Specify an init container to clone the repository + initContainers: + - name: "provision-git" + # Use an image that includes git + image: bitnami/git + # Mount the same volume we also use in the main container + volumeMounts: + - mountPath: "/repo/" + name: repository + # Specify the clone command and clone into the volume, mounted at /repo/ + command: + - git + - clone + - "https://github.com/we45/Vulnerable-Flask-App" + - /repo/flask-app + # Parameterize the semgrep scan itself + scanType: "semgrep" + parameters: + - "-c" + - "p/ci" + - "/repo/flask-app" +``` + +If your repository requires authentication to clone, you will have to give the initContainer access to some method of authentication. +This could be a personal access token ([GitHub](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token), [GitLab](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)), project access token ([GitLab](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html)), deploy key ([GitHub](https://docs.github.com/en/developers/overview/managing-deploy-keys#deploy-keys) / [GitLab](https://docs.gitlab.com/ee/user/project/deploy_keys/)), deploy token ([GitLab](https://docs.gitlab.com/ee/user/project/deploy_tokens/)), or a server-to-server token ([GitHub](https://docs.github.com/en/developers/overview/managing-deploy-keys#server-to-server-tokens)). +Due to the large variety of options, we do not provide documentation for all of them here. +Refer to the linked documentation for details on the different methods, and remember to use [Kubernetes secrets](https://kubernetes.io/docs/concepts/configuration/secret/) to manage keys and tokens. + +## Cascading Rules +By default, the semgrep scanner does not install any [cascading rules](docs/hooks/cascading-scans), as some aspects of the semgrep scan (like the used ruleset) should be customized. +However, you can easily create your own cascading rule, for example to run semgrep on the output of [git-repo-scanner](docs/scanners/git-repo-scanner). +As a starting point, consider the following cascading rule to scan all public GitHub repositories found by git-repo-scanner using the p/ci ruleset of semgrep: + +```yaml +apiVersion: "cascading.securecodebox.io/v1" +kind: CascadingRule +metadata: + name: "semgrep-public-github-repos" + labels: + securecodebox.io/invasive: non-invasive + securecodebox.io/intensive: medium +spec: + matches: + anyOf: + # We want to scan public GitHub repositories. Change "public" to "private" to scan private repos instead + - name: "GitHub Repo" + attributes: + visibility: public + scanSpec: + # Configure the scanSpec for semgrep + scanType: "semgrep" + parameters: + - "-c" + - "p/ci" # Change this to use a different rule set + - "/repo/" + volumes: + - name: repo + emptyDir: {} + volumeMounts: + - name: repo + mountPath: "/repo/" + initContainers: + - name: "git-clone" + image: bitnami/git + # The command assumes that GITHUB_TOKEN contains a GitHub access token with access to the repository. + # GITHUB_TOKEN is set below in the "env" section. + # If you do not wan to use an access token, remove it from the URL below. + command: + - git + - clone + - "https://$(GITHUB_TOKEN)@github.com/{{{attributes.full_name}}}" + - /repo/ + volumeMounts: + - mountPath: "/repo/" + name: repo + # Load the GITHUB_TOKEN from the kubernetes secret with the name "github-access-token" + # Create this secret using, for example: + # echo -n 'YOUR TOKEN GOES HERE' > github-token.txt && kubectl create secret generic github-access-token --from-file=token=github-token.txt + # IMPORTANT: Ensure that github-token.txt does not have a new line at the end of the file. This is automatically done by using "echo -n" to create it. + # However, if you create it with an editor, some editors (most notably, vim) will create hidden newlines at the end of files, which will cause issues. + env: + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: github-access-token + key: token +``` + +Use this configuration as a baseline for your own rules. + +## Requirements + +Kubernetes: `>=v1.11.0-0` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| cascadingRules.enabled | bool | `true` | Enables or disables the installation of the default cascading rules for this scanner | +| parser.backoffLimit | int | `3` | | +| parser.env | list | `[]` | | +| parser.image.pullPolicy | string | `"IfNotPresent"` | | +| parser.image.repository | string | `"securecodebox/parser-semgrep"` | | +| parser.image.tag | string | `nil` | | +| scanner.backoffLimit | int | `3` | | +| scanner.env | list | `[]` | | +| scanner.extraContainers | list | `[]` | | +| scanner.extraVolumeMounts | list | `[]` | | +| scanner.extraVolumes | list | `[]` | | +| scanner.image.pullPolicy | string | `"IfNotPresent"` | | +| scanner.image.repository | string | `"docker.io/returntocorp/semgrep"` | | +| scanner.image.tag | string | `nil` | | +| scanner.resources | object | `{}` | | +| scanner.securityContext.allowPrivilegeEscalation | bool | `false` | | +| scanner.securityContext.capabilities.drop[0] | string | `"all"` | | +| scanner.securityContext.privileged | bool | `false` | | +| scanner.securityContext.readOnlyRootFilesystem | bool | `false` | | +| scanner.securityContext.runAsNonRoot | bool | `true` | | +| scanner.ttlSecondsAfterFinished | string | `nil` | | + +## Contributing + +Contributions are welcome and extremely helpful 🙌 +Please have a look at [Contributing](./CONTRIBUTING.md) + +## Community + +You are welcome, please join us on... 👋 + +- [GitHub][scb-github] +- [Slack][scb-slack] +- [Twitter][scb-twitter] + +secureCodeBox is an official [OWASP][scb-owasp] project. + +## License +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +Code of secureCodeBox is licensed under the [Apache License 2.0][scb-license]. + +[scb-owasp]: https://www.owasp.org/index.php/OWASP_secureCodeBox +[scb-docs]: https://docs.securecodebox.io/ +[scb-site]: https://www.securecodebox.io/ +[scb-github]: https://github.com/secureCodeBox/ +[scb-twitter]: https://twitter.com/secureCodeBox +[scb-slack]: https://join.slack.com/t/securecodebox/shared_invite/enQtNDU3MTUyOTM0NTMwLTBjOWRjNjVkNGEyMjQ0ZGMyNDdlYTQxYWQ4MzNiNGY3MDMxNThkZjJmMzY2NDRhMTk3ZWM3OWFkYmY1YzUxNTU +[scb-license]: https://github.com/secureCodeBox/secureCodeBox/blob/master/LICENSE + diff --git a/scanners/semgrep/docs/README.DockerHub-Parser.md b/scanners/semgrep/docs/README.DockerHub-Parser.md new file mode 100644 index 0000000000..a0525748f6 --- /dev/null +++ b/scanners/semgrep/docs/README.DockerHub-Parser.md @@ -0,0 +1,84 @@ + + + +

+ License Apache-2.0 + GitHub release (latest SemVer) + OWASP Incubator Project + Artifact HUB + GitHub Repo stars + Twitter Follower +

+ +## What is OWASP secureCodeBox? + +

+ secureCodeBox Logo +

+ +_[OWASP secureCodeBox][scb-github]_ is an automated and scalable open source solution that can be used to integrate various *security vulnerability scanners* with a simple and lightweight interface. The _secureCodeBox_ mission is to support *DevSecOps* Teams to make it easy to automate security vulnerability testing in different scenarios. + +With the _secureCodeBox_ we provide a toolchain for continuous scanning of applications to find the low-hanging fruit issues early in the development process and free the resources of the penetration tester to concentrate on the major security issues. + +The secureCodeBox project is running on [Kubernetes](https://kubernetes.io/). To install it you need [Helm](https://helm.sh), a package manager for Kubernetes. It is also possible to start the different integrated security vulnerability scanners based on a docker infrastructure. + +### Quickstart with secureCodeBox on kubernetes + +You can find resources to help you get started on our [documentation website](https://docs.securecodebox.io) including instruction on how to [install the secureCodeBox project](https://docs.securecodebox.io/docs/getting-started/installation) and guides to help you [run your first scans](https://docs.securecodebox.io/docs/getting-started/first-scans) with it. + +## Supported Tags +- `latest` (represents the latest stable release build) +- tagged releases, e.g. `0.70.0` + +## How to use this image +This `parser` image is intended to work in combination with the corresponding security scanner docker image to parse the `findings` results. For more information details please take a look at the documentation page: https://docs.securecodebox.io/docs/scanners/semgrep. + +```bash +docker pull securecodebox/parser-semgrep +``` + +## What is Semgrep? +Semgrep ("semantic grep") is a static source code analyzer that can be used to search for specific patterns in code. +It allows you to either [write your own rules](https://semgrep.dev/learn), or use one of the [many pre-defined rulesets](https://semgrep.dev/r) curated by the semgrep team. + +To learn more about semgrep, visit [semgrep.dev](https://semgrep.dev). + +## Community + +You are welcome, please join us on... 👋 + +- [GitHub][scb-github] +- [Slack][scb-slack] +- [Twitter][scb-twitter] + +secureCodeBox is an official [OWASP][scb-owasp] project. + +## License +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. + +[scb-owasp]: https://www.owasp.org/index.php/OWASP_secureCodeBox +[scb-docs]: https://docs.securecodebox.io/ +[scb-site]: https://www.securecodebox.io/ +[scb-github]: https://github.com/secureCodeBox/ +[scb-twitter]: https://twitter.com/secureCodeBox +[scb-slack]: https://join.slack.com/t/securecodebox/shared_invite/enQtNDU3MTUyOTM0NTMwLTBjOWRjNjVkNGEyMjQ0ZGMyNDdlYTQxYWQ4MzNiNGY3MDMxNThkZjJmMzY2NDRhMTk3ZWM3OWFkYmY1YzUxNTU +[scb-license]: https://github.com/secureCodeBox/secureCodeBox/blob/master/LICENSE + diff --git a/scanners/semgrep/docs/README.DockerHub-Scanner.md b/scanners/semgrep/docs/README.DockerHub-Scanner.md new file mode 100644 index 0000000000..3fecf6cb5c --- /dev/null +++ b/scanners/semgrep/docs/README.DockerHub-Scanner.md @@ -0,0 +1,199 @@ + + + +

+ License Apache-2.0 + GitHub release (latest SemVer) + OWASP Incubator Project + Artifact HUB + GitHub Repo stars + Twitter Follower +

+ +## What is OWASP secureCodeBox? + +

+ secureCodeBox Logo +

+ +_[OWASP secureCodeBox][scb-github]_ is an automated and scalable open source solution that can be used to integrate various *security vulnerability scanners* with a simple and lightweight interface. The _secureCodeBox_ mission is to support *DevSecOps* Teams to make it easy to automate security vulnerability testing in different scenarios. + +With the _secureCodeBox_ we provide a toolchain for continuous scanning of applications to find the low-hanging fruit issues early in the development process and free the resources of the penetration tester to concentrate on the major security issues. + +The secureCodeBox project is running on [Kubernetes](https://kubernetes.io/). To install it you need [Helm](https://helm.sh), a package manager for Kubernetes. It is also possible to start the different integrated security vulnerability scanners based on a docker infrastructure. + +### Quickstart with secureCodeBox on kubernetes + +You can find resources to help you get started on our [documentation website](https://docs.securecodebox.io) including instruction on how to [install the secureCodeBox project](https://docs.securecodebox.io/docs/getting-started/installation) and guides to help you [run your first scans](https://docs.securecodebox.io/docs/getting-started/first-scans) with it. + +## Supported Tags +- `latest` (represents the latest stable release build) +- tagged releases, e.g. `0.70.0` + +## How to use this image +This `scanner` image is intended to work in combination with the corresponding `parser` image to parse the scanner `findings` to generic secureCodeBox results. For more information details please take a look at the [project page][scb-docs] or [documentation page][https://docs.securecodebox.io/docs/scanners/semgrep]. + +```bash +docker pull securecodebox/scanner-semgrep +``` + +## What is Semgrep? +Semgrep ("semantic grep") is a static source code analyzer that can be used to search for specific patterns in code. +It allows you to either [write your own rules](https://semgrep.dev/learn), or use one of the [many pre-defined rulesets](https://semgrep.dev/r) curated by the semgrep team. + +To learn more about semgrep, visit [semgrep.dev](https://semgrep.dev). + +## Scanner Configuration + +Semgrep requires one or more ruleset(s) to run its scans. +Refer to the [semgrep rule database](https://semgrep.dev/r) for more details. +A good starting point would be [p/ci](https://semgrep.dev/p/ci) (for security checks with a low false-positive rate) or [p/security-audit](https://semgrep.dev/p/security-audit) (for a more comprehensive security audit, which may include more false-positive results). + +Semgrep needs access to the source code to run its analysis. +To use it with secureCodeBox, you thus need a way to provision the data into the scan container. +The recommended method is to use `initContainers` to clone a VCS repository. +The simplest example, using a public Git repository from GitHub, looks like this: + +```yaml +apiVersion: "execution.securecodebox.io/v1" +kind: Scan +metadata: + name: "semgrep-vulnerable-flask-app" +spec: + # Specify a Kubernetes volume that will be shared between the scanner and the initContainer + volumes: + - name: repository + emptyDir: {} + # Mount the volume in the scan container + volumeMounts: + - mountPath: "/repo/" + name: repository + # Specify an init container to clone the repository + initContainers: + - name: "provision-git" + # Use an image that includes git + image: bitnami/git + # Mount the same volume we also use in the main container + volumeMounts: + - mountPath: "/repo/" + name: repository + # Specify the clone command and clone into the volume, mounted at /repo/ + command: + - git + - clone + - "https://github.com/we45/Vulnerable-Flask-App" + - /repo/flask-app + # Parameterize the semgrep scan itself + scanType: "semgrep" + parameters: + - "-c" + - "p/ci" + - "/repo/flask-app" +``` + +If your repository requires authentication to clone, you will have to give the initContainer access to some method of authentication. +This could be a personal access token ([GitHub](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token), [GitLab](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)), project access token ([GitLab](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html)), deploy key ([GitHub](https://docs.github.com/en/developers/overview/managing-deploy-keys#deploy-keys) / [GitLab](https://docs.gitlab.com/ee/user/project/deploy_keys/)), deploy token ([GitLab](https://docs.gitlab.com/ee/user/project/deploy_tokens/)), or a server-to-server token ([GitHub](https://docs.github.com/en/developers/overview/managing-deploy-keys#server-to-server-tokens)). +Due to the large variety of options, we do not provide documentation for all of them here. +Refer to the linked documentation for details on the different methods, and remember to use [Kubernetes secrets](https://kubernetes.io/docs/concepts/configuration/secret/) to manage keys and tokens. + +## Cascading Rules +By default, the semgrep scanner does not install any [cascading rules](docs/hooks/cascading-scans), as some aspects of the semgrep scan (like the used ruleset) should be customized. +However, you can easily create your own cascading rule, for example to run semgrep on the output of [git-repo-scanner](docs/scanners/git-repo-scanner). +As a starting point, consider the following cascading rule to scan all public GitHub repositories found by git-repo-scanner using the p/ci ruleset of semgrep: + +```yaml +apiVersion: "cascading.securecodebox.io/v1" +kind: CascadingRule +metadata: + name: "semgrep-public-github-repos" + labels: + securecodebox.io/invasive: non-invasive + securecodebox.io/intensive: medium +spec: + matches: + anyOf: + # We want to scan public GitHub repositories. Change "public" to "private" to scan private repos instead + - name: "GitHub Repo" + attributes: + visibility: public + scanSpec: + # Configure the scanSpec for semgrep + scanType: "semgrep" + parameters: + - "-c" + - "p/ci" # Change this to use a different rule set + - "/repo/" + volumes: + - name: repo + emptyDir: {} + volumeMounts: + - name: repo + mountPath: "/repo/" + initContainers: + - name: "git-clone" + image: bitnami/git + # The command assumes that GITHUB_TOKEN contains a GitHub access token with access to the repository. + # GITHUB_TOKEN is set below in the "env" section. + # If you do not wan to use an access token, remove it from the URL below. + command: + - git + - clone + - "https://$(GITHUB_TOKEN)@github.com/{{{attributes.full_name}}}" + - /repo/ + volumeMounts: + - mountPath: "/repo/" + name: repo + # Load the GITHUB_TOKEN from the kubernetes secret with the name "github-access-token" + # Create this secret using, for example: + # echo -n 'YOUR TOKEN GOES HERE' > github-token.txt && kubectl create secret generic github-access-token --from-file=token=github-token.txt + # IMPORTANT: Ensure that github-token.txt does not have a new line at the end of the file. This is automatically done by using "echo -n" to create it. + # However, if you create it with an editor, some editors (most notably, vim) will create hidden newlines at the end of files, which will cause issues. + env: + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: github-access-token + key: token +``` + +Use this configuration as a baseline for your own rules. + +## Community + +You are welcome, please join us on... 👋 + +- [GitHub][scb-github] +- [Slack][scb-slack] +- [Twitter][scb-twitter] + +secureCodeBox is an official [OWASP][scb-owasp] project. + +## License +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. + +[scb-owasp]: https://www.owasp.org/index.php/OWASP_secureCodeBox +[scb-docs]: https://docs.securecodebox.io/ +[scb-site]: https://www.securecodebox.io/ +[scb-github]: https://github.com/secureCodeBox/ +[scb-twitter]: https://twitter.com/secureCodeBox +[scb-slack]: https://join.slack.com/t/securecodebox/shared_invite/enQtNDU3MTUyOTM0NTMwLTBjOWRjNjVkNGEyMjQ0ZGMyNDdlYTQxYWQ4MzNiNGY3MDMxNThkZjJmMzY2NDRhMTk3ZWM3OWFkYmY1YzUxNTU +[scb-license]: https://github.com/secureCodeBox/secureCodeBox/blob/master/LICENSE + diff --git a/scanners/semgrep/examples/vulnerable-flask-app/findings.yaml b/scanners/semgrep/examples/vulnerable-flask-app/findings.yaml new file mode 100644 index 0000000000..f601859d66 --- /dev/null +++ b/scanners/semgrep/examples/vulnerable-flask-app/findings.yaml @@ -0,0 +1,166 @@ +[ + { + "name": "javascript.lang.correctness.useless-eqeq.eqeq-is-bad", + "location": "/test/flask/app/static/loader.js:91-91", + "description": "Detected a useless comparison operation `0 == 0` or `0 != 0`. This operation is always true. If testing for floating point NaN, use `math.isnan`, or `cmath.isnan` if the number is complex.", + "category": "correctness", + "severity": "HIGH", + "attributes": { + "cwe": null, + "owasp_category": null, + "references": null, + "rule_source": "https://semgrep.dev/r/javascript.lang.correctness.useless-eqeq.eqeq-is-bad", + "matching_lines": "K.h.i.Bf=function(b,c){var d=0,e=0,f=!1;b=K.h.i.Ta(b,c).split(K.h.i.Sl);for(c=0;cK.h.i.dl?K.h.i.O.Va:K.h.i.O.Ua};K.h.i.vq=function(b,c){return K.h.i.Bf(b,c)==K.h.i.O.Va};K.h.i.ht=function(b,c){b&&(c=K.h.i.Dl(c))&&(b.style.textAlign=c==K.h.i.O.Va?K.h.i.ec:K.h.i.cc,b.dir=c==K.h.i.O.Va?\"rtl\":\"ltr\")};" + }, + "id": "ee0afb67-a248-4bee-9863-68573bc900a9", + "parsed_at": "2021-10-15T09:05:12.769Z" + }, + { + "name": "python.flask.security.dangerous-template-string.dangerous-template-string", + "location": "/test/flask/app/app.py:103-114", + "description": "Found a template created with string formatting. This is susceptible to server-side template injection and cross-site scripting attacks.", + "category": "security", + "severity": "HIGH", + "attributes": { + "cwe": "CWE-96: Improper Neutralization of Directives in Statically Saved Code ('Static Code Injection')", + "owasp_category": "A1: Injection", + "references": [ + "https://nvisium.com/blog/2016/03/09/exploring-ssti-in-flask-jinja2.html", + "https://pequalsnp-team.github.io/cheatsheet/flask-jinja2-ssti" + ], + "rule_source": "https://semgrep.dev/r/python.flask.security.dangerous-template-string.dangerous-template-string", + "matching_lines": " template = '''\n \n Error\n \n \n

Oops that page doesn't exist!!

\n

%s

\n \n \n ''' % request.url\n\n return render_template_string(template, dir = dir, help = help, locals = locals),404" + }, + "id": "496862b3-6f61-4119-a5d7-f3ddec8ddc7e", + "parsed_at": "2021-10-15T09:05:12.769Z" + }, + { + "name": "python.flask.security.dangerous-template-string.dangerous-template-string", + "location": "/test/flask/app/app.py:271-281", + "description": "Found a template created with string formatting. This is susceptible to server-side template injection and cross-site scripting attacks.", + "category": "security", + "severity": "HIGH", + "attributes": { + "cwe": "CWE-96: Improper Neutralization of Directives in Statically Saved Code ('Static Code Injection')", + "owasp_category": "A1: Injection", + "references": [ + "https://nvisium.com/blog/2016/03/09/exploring-ssti-in-flask-jinja2.html", + "https://pequalsnp-team.github.io/cheatsheet/flask-jinja2-ssti" + ], + "rule_source": "https://semgrep.dev/r/python.flask.security.dangerous-template-string.dangerous-template-string", + "matching_lines": " template = '''\n \n Error\n \n \n

Oops Error Occurred

\n

%s

\n \n \n ''' % str(e)\n return render_template_string(template, dir=dir, help=help, locals=locals), 404" + }, + "id": "ded6aac2-e6bf-411a-9696-f6d70e3f9750", + "parsed_at": "2021-10-15T09:05:12.769Z" + }, + { + "name": "python.flask.security.insecure-deserialization.insecure-deserialization", + "location": "/test/flask/app/app.py:329-329", + "description": "Detected the use of an insecure deserialization library in a Flask route. These libraries are prone to code execution vulnerabilities. Ensure user data does not enter this function. To fix this, try to avoid serializing whole objects. Consider instead using a serializer such as JSON.", + "category": "security", + "severity": "HIGH", + "attributes": { + "cwe": "CWE-502: Deserialization of Untrusted Data", + "owasp_category": "A8: Insecure Deserialization", + "references": [ + "https://docs.python.org/3/library/pickle.html" + ], + "rule_source": "https://semgrep.dev/r/python.flask.security.insecure-deserialization.insecure-deserialization", + "matching_lines": " ydata = yaml.load(y)" + }, + "id": "dfdf9a67-1ec3-40d8-8b5f-862ca5ebe3db", + "parsed_at": "2021-10-15T09:05:12.769Z" + }, + { + "name": "python.lang.security.insecure-hash-algorithms.insecure-hash-algorithm-md5", + "location": "/test/flask/app/app.py:141-141", + "description": "Detected MD5 hash algorithm which is considered insecure. MD5 is not collision resistant and is therefore not suitable as a cryptographic signature. Use SHA256 or SHA3 instead.", + "category": "security", + "severity": "MEDIUM", + "attributes": { + "cwe": "CWE-327: Use of a Broken or Risky Cryptographic Algorithm", + "owasp_category": "A3: Sensitive Data Exposure", + "references": [ + "https://tools.ietf.org/html/rfc6151", + "https://crypto.stackexchange.com/questions/44151/how-does-the-flame-malware-take-advantage-of-md5-collision", + "https://pycryptodome.readthedocs.io/en/latest/src/hash/sha3_256.html" + ], + "rule_source": "https://semgrep.dev/r/python.lang.security.insecure-hash-algorithms.insecure-hash-algorithm-md5", + "matching_lines": " hash_pass = hashlib.md5(password).hexdigest()" + }, + "id": "4524f52b-7cb8-4a5b-8a89-12c188efc92e", + "parsed_at": "2021-10-15T09:05:12.769Z" + }, + { + "name": "python.requests.security.disabled-cert-validation.disabled-cert-validation", + "location": "/test/flask/tests/e2e_zap.py:17-18", + "description": "Certificate verification has been explicitly disabled. This permits insecure connections to insecure servers. Re-enable certification validation.", + "category": "security", + "severity": "HIGH", + "attributes": { + "cwe": "CWE-295: Improper Certificate Validation", + "owasp_category": "A3: Sensitive Data Exposure", + "references": [ + "https://stackoverflow.com/questions/41740361/is-it-safe-to-disable-ssl-certificate-verification-in-pythonss-requests-lib" + ], + "rule_source": "https://semgrep.dev/r/python.requests.security.disabled-cert-validation.disabled-cert-validation", + "matching_lines": "login = requests.post(target_url + '/login',\n proxies=proxies, json=auth_dict, verify=False)" + }, + "id": "18a0cd4b-4b43-4017-8d90-1e6de5dfde76", + "parsed_at": "2021-10-15T09:05:12.769Z" + }, + { + "name": "python.requests.security.disabled-cert-validation.disabled-cert-validation", + "location": "/test/flask/tests/e2e_zap.py:28-29", + "description": "Certificate verification has been explicitly disabled. This permits insecure connections to insecure servers. Re-enable certification validation.", + "category": "security", + "severity": "HIGH", + "attributes": { + "cwe": "CWE-295: Improper Certificate Validation", + "owasp_category": "A3: Sensitive Data Exposure", + "references": [ + "https://stackoverflow.com/questions/41740361/is-it-safe-to-disable-ssl-certificate-verification-in-pythonss-requests-lib" + ], + "rule_source": "https://semgrep.dev/r/python.requests.security.disabled-cert-validation.disabled-cert-validation", + "matching_lines": " get_cust_id = requests.get(\n target_url + '/get/2', proxies=proxies, headers=auth_header, verify=False)" + }, + "id": "6ffd9ab4-f736-473b-88b3-24f1e1103ec6", + "parsed_at": "2021-10-15T09:05:12.769Z" + }, + { + "name": "python.requests.security.disabled-cert-validation.disabled-cert-validation", + "location": "/test/flask/tests/e2e_zap.py:36-37", + "description": "Certificate verification has been explicitly disabled. This permits insecure connections to insecure servers. Re-enable certification validation.", + "category": "security", + "severity": "HIGH", + "attributes": { + "cwe": "CWE-295: Improper Certificate Validation", + "owasp_category": "A3: Sensitive Data Exposure", + "references": [ + "https://stackoverflow.com/questions/41740361/is-it-safe-to-disable-ssl-certificate-verification-in-pythonss-requests-lib" + ], + "rule_source": "https://semgrep.dev/r/python.requests.security.disabled-cert-validation.disabled-cert-validation", + "matching_lines": " fetch_customer_post = requests.post(\n target_url + '/fetch/customer', json=post, proxies=proxies, headers=auth_header, verify=False)" + }, + "id": "b9d7d55c-d314-440d-a3dc-e41a5dd2ec0f", + "parsed_at": "2021-10-15T09:05:12.769Z" + }, + { + "name": "python.requests.security.disabled-cert-validation.disabled-cert-validation", + "location": "/test/flask/tests/e2e_zap.py:44-45", + "description": "Certificate verification has been explicitly disabled. This permits insecure connections to insecure servers. Re-enable certification validation.", + "category": "security", + "severity": "HIGH", + "attributes": { + "cwe": "CWE-295: Improper Certificate Validation", + "owasp_category": "A3: Sensitive Data Exposure", + "references": [ + "https://stackoverflow.com/questions/41740361/is-it-safe-to-disable-ssl-certificate-verification-in-pythonss-requests-lib" + ], + "rule_source": "https://semgrep.dev/r/python.requests.security.disabled-cert-validation.disabled-cert-validation", + "matching_lines": " search_customer_username = requests.post(\n target_url + '/search', json=search, proxies=proxies, headers=auth_header, verify=False)" + }, + "id": "f82d51de-8ce7-43fb-a225-6b7662418ea9", + "parsed_at": "2021-10-15T09:05:12.769Z" + } +] diff --git a/scanners/semgrep/examples/vulnerable-flask-app/scan.yaml b/scanners/semgrep/examples/vulnerable-flask-app/scan.yaml new file mode 100644 index 0000000000..ebdef2e954 --- /dev/null +++ b/scanners/semgrep/examples/vulnerable-flask-app/scan.yaml @@ -0,0 +1,27 @@ +apiVersion: "execution.securecodebox.io/v1" +kind: Scan +metadata: + name: "semgrep-vulnerable-flask-app" +spec: + volumes: + - name: test-dir + emptyDir: {} + volumeMounts: + - mountPath: "/test/" + name: test-dir + scanType: "semgrep" + parameters: + - "-c" + - "p/ci" + - "/test/flask" + initContainers: + - name: "provision-git" + image: bitnami/git + command: + - git + - clone + - "https://github.com/we45/Vulnerable-Flask-App" + - /test/flask + volumeMounts: + - mountPath: "/test/" + name: test-dir diff --git a/scanners/semgrep/examples/vulnerable-flask-app/semgrep-findings.json b/scanners/semgrep/examples/vulnerable-flask-app/semgrep-findings.json new file mode 100644 index 0000000000..6a0afc5192 --- /dev/null +++ b/scanners/semgrep/examples/vulnerable-flask-app/semgrep-findings.json @@ -0,0 +1,458 @@ +{ + "errors": [], + "results": [ + { + "check_id": "javascript.lang.correctness.useless-eqeq.eqeq-is-bad", + "end": { + "col": 204, + "line": 91, + "offset": 40011 + }, + "extra": { + "is_ignored": false, + "lines": "K.h.i.Bf=function(b,c){var d=0,e=0,f=!1;b=K.h.i.Ta(b,c).split(K.h.i.Sl);for(c=0;cK.h.i.dl?K.h.i.O.Va:K.h.i.O.Ua};K.h.i.vq=function(b,c){return K.h.i.Bf(b,c)==K.h.i.O.Va};K.h.i.ht=function(b,c){b&&(c=K.h.i.Dl(c))&&(b.style.textAlign=c==K.h.i.O.Va?K.h.i.ec:K.h.i.cc,b.dir=c==K.h.i.O.Va?\"rtl\":\"ltr\")};", + "message": "Detected a useless comparison operation `0 == 0` or `0 != 0`. This operation is always true. If testing for floating point NaN, use `math.isnan`, or `cmath.isnan` if the number is complex.", + "metadata": { + "category": "correctness", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "source": "https://semgrep.dev/r/javascript.lang.correctness.useless-eqeq.eqeq-is-bad", + "technology": [ + "javascript" + ] + }, + "metavars": { + "$X": { + "abstract_content": "0", + "end": { + "col": 201, + "line": 91, + "offset": 40008 + }, + "start": { + "col": 200, + "line": 91, + "offset": 40007 + }, + "unique_id": { + "md5sum": "de2eee18976eddef2fddc8f55c572c38", + "type": "AST" + } + } + }, + "severity": "ERROR" + }, + "path": "/test/flask/app/static/loader.js", + "start": { + "col": 200, + "line": 91, + "offset": 40007 + } + }, + { + "check_id": "python.flask.security.dangerous-template-string.dangerous-template-string", + "end": { + "col": 89, + "line": 114, + "offset": 3391 + }, + "extra": { + "is_ignored": false, + "lines": " template = '''\n \n Error\n \n \n

Oops that page doesn't exist!!

\n

%s

\n \n \n ''' % request.url\n\n return render_template_string(template, dir = dir, help = help, locals = locals),404", + "message": "Found a template created with string formatting. This is susceptible to server-side template injection and cross-site scripting attacks.", + "metadata": { + "category": "security", + "cwe": "CWE-96: Improper Neutralization of Directives in Statically Saved Code ('Static Code Injection')", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A1: Injection", + "references": [ + "https://nvisium.com/blog/2016/03/09/exploring-ssti-in-flask-jinja2.html", + "https://pequalsnp-team.github.io/cheatsheet/flask-jinja2-ssti" + ], + "source": "https://semgrep.dev/r/python.flask.security.dangerous-template-string.dangerous-template-string", + "technology": [ + "flask" + ] + }, + "metavars": { + "$MORE": { + "abstract_content": "404", + "end": { + "col": 89, + "line": 114, + "offset": 3391 + }, + "start": { + "col": 86, + "line": 114, + "offset": 3388 + }, + "unique_id": { + "md5sum": "98ac636d2f0eba848fbb945ae7e020d0", + "type": "AST" + } + }, + "$S": { + "abstract_content": "request.url", + "end": { + "col": 22, + "line": 112, + "offset": 3301 + }, + "start": { + "col": 11, + "line": 112, + "offset": 3290 + }, + "unique_id": { + "md5sum": "0efad3f975cb551a7f5fa183cf36f778", + "type": "AST" + } + }, + "$V": { + "abstract_content": "template", + "end": { + "col": 13, + "line": 103, + "offset": 3124 + }, + "start": { + "col": 5, + "line": 103, + "offset": 3116 + }, + "unique_id": { + "sid": 35, + "type": "id" + } + } + }, + "severity": "ERROR" + }, + "path": "/test/flask/app/app.py", + "start": { + "col": 5, + "line": 103, + "offset": 3116 + } + }, + { + "check_id": "python.flask.security.dangerous-template-string.dangerous-template-string", + "end": { + "col": 100, + "line": 281, + "offset": 10412 + }, + "extra": { + "is_ignored": false, + "lines": " template = '''\n \n Error\n \n \n

Oops Error Occurred

\n

%s

\n \n \n ''' % str(e)\n return render_template_string(template, dir=dir, help=help, locals=locals), 404", + "message": "Found a template created with string formatting. This is susceptible to server-side template injection and cross-site scripting attacks.", + "metadata": { + "category": "security", + "cwe": "CWE-96: Improper Neutralization of Directives in Statically Saved Code ('Static Code Injection')", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A1: Injection", + "references": [ + "https://nvisium.com/blog/2016/03/09/exploring-ssti-in-flask-jinja2.html", + "https://pequalsnp-team.github.io/cheatsheet/flask-jinja2-ssti" + ], + "source": "https://semgrep.dev/r/python.flask.security.dangerous-template-string.dangerous-template-string", + "technology": [ + "flask" + ] + }, + "metavars": { + "$MORE": { + "abstract_content": "404", + "end": { + "col": 100, + "line": 281, + "offset": 10412 + }, + "start": { + "col": 97, + "line": 281, + "offset": 10409 + }, + "unique_id": { + "md5sum": "98ac636d2f0eba848fbb945ae7e020d0", + "type": "AST" + } + }, + "$S": { + "abstract_content": "str(e)", + "end": { + "col": 37, + "line": 280, + "offset": 10312 + }, + "start": { + "col": 31, + "line": 280, + "offset": 10306 + }, + "unique_id": { + "md5sum": "b61b86088a47df3faba836438ca4c0f1", + "type": "AST" + } + }, + "$V": { + "abstract_content": "template", + "end": { + "col": 29, + "line": 271, + "offset": 9971 + }, + "start": { + "col": 21, + "line": 271, + "offset": 9963 + }, + "unique_id": { + "sid": 82, + "type": "id" + } + } + }, + "severity": "ERROR" + }, + "path": "/test/flask/app/app.py", + "start": { + "col": 21, + "line": 271, + "offset": 9963 + } + }, + { + "check_id": "python.flask.security.insecure-deserialization.insecure-deserialization", + "end": { + "col": 29, + "line": 329, + "offset": 11793 + }, + "extra": { + "is_ignored": false, + "lines": " ydata = yaml.load(y)", + "message": "Detected the use of an insecure deserialization library in a Flask route. These libraries are prone to code execution vulnerabilities. Ensure user data does not enter this function. To fix this, try to avoid serializing whole objects. Consider instead using a serializer such as JSON.", + "metadata": { + "category": "security", + "cwe": "CWE-502: Deserialization of Untrusted Data", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A8: Insecure Deserialization", + "references": [ + "https://docs.python.org/3/library/pickle.html" + ], + "source": "https://semgrep.dev/r/python.flask.security.insecure-deserialization.insecure-deserialization", + "technology": [ + "flask" + ] + }, + "metavars": { + "$X": { + "abstract_content": "yaml_hammer", + "end": { + "col": 16, + "line": 316, + "offset": 11366 + }, + "start": { + "col": 5, + "line": 316, + "offset": 11355 + }, + "unique_id": { + "md5sum": "5af53207c013e999e2656457108b67b4", + "type": "AST" + } + } + }, + "severity": "ERROR" + }, + "path": "/test/flask/app/app.py", + "start": { + "col": 17, + "line": 329, + "offset": 11781 + } + }, + { + "check_id": "python.lang.security.insecure-hash-algorithms.insecure-hash-algorithm-md5", + "end": { + "col": 46, + "line": 141, + "offset": 4329 + }, + "extra": { + "is_ignored": false, + "lines": " hash_pass = hashlib.md5(password).hexdigest()", + "message": "Detected MD5 hash algorithm which is considered insecure. MD5 is not collision resistant and is therefore not suitable as a cryptographic signature. Use SHA256 or SHA3 instead.", + "metadata": { + "asvs": { + "control_id": "6.2.2 Insecure Custom Algorithm", + "control_url": "https://github.com/OWASP/ASVS/blob/master/4.0/en/0x14-V6-Cryptography.md#v62-algorithms", + "section": "V6 Stored Cryptography Verification Requirements", + "version": "4" + }, + "bandit-code": "B303", + "category": "security", + "cwe": "CWE-327: Use of a Broken or Risky Cryptographic Algorithm", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A3: Sensitive Data Exposure", + "references": [ + "https://tools.ietf.org/html/rfc6151", + "https://crypto.stackexchange.com/questions/44151/how-does-the-flame-malware-take-advantage-of-md5-collision", + "https://pycryptodome.readthedocs.io/en/latest/src/hash/sha3_256.html" + ], + "source": "https://semgrep.dev/r/python.lang.security.insecure-hash-algorithms.insecure-hash-algorithm-md5", + "source-rule-url": "https://github.com/PyCQA/bandit/blob/d5f8fa0d89d7b11442fc6ec80ca42953974354c8/bandit/blacklists/calls.py#L59", + "technology": [ + "python" + ] + }, + "metavars": {}, + "severity": "WARNING" + }, + "path": "/test/flask/app/app.py", + "start": { + "col": 25, + "line": 141, + "offset": 4308 + } + }, + { + "check_id": "python.requests.security.disabled-cert-validation.disabled-cert-validation", + "end": { + "col": 69, + "line": 18, + "offset": 435 + }, + "extra": { + "is_ignored": false, + "lines": "login = requests.post(target_url + '/login',\n proxies=proxies, json=auth_dict, verify=False)", + "message": "Certificate verification has been explicitly disabled. This permits insecure connections to insecure servers. Re-enable certification validation.", + "metadata": { + "category": "security", + "cwe": "CWE-295: Improper Certificate Validation", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A3: Sensitive Data Exposure", + "references": [ + "https://stackoverflow.com/questions/41740361/is-it-safe-to-disable-ssl-certificate-verification-in-pythonss-requests-lib" + ], + "source": "https://semgrep.dev/r/python.requests.security.disabled-cert-validation.disabled-cert-validation", + "technology": [ + "requests" + ] + }, + "metavars": {}, + "severity": "ERROR" + }, + "path": "/test/flask/tests/e2e_zap.py", + "start": { + "col": 9, + "line": 17, + "offset": 330 + } + }, + { + "check_id": "python.requests.security.disabled-cert-validation.disabled-cert-validation", + "end": { + "col": 83, + "line": 29, + "offset": 765 + }, + "extra": { + "is_ignored": false, + "lines": " get_cust_id = requests.get(\n target_url + '/get/2', proxies=proxies, headers=auth_header, verify=False)", + "message": "Certificate verification has been explicitly disabled. This permits insecure connections to insecure servers. Re-enable certification validation.", + "metadata": { + "category": "security", + "cwe": "CWE-295: Improper Certificate Validation", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A3: Sensitive Data Exposure", + "references": [ + "https://stackoverflow.com/questions/41740361/is-it-safe-to-disable-ssl-certificate-verification-in-pythonss-requests-lib" + ], + "source": "https://semgrep.dev/r/python.requests.security.disabled-cert-validation.disabled-cert-validation", + "technology": [ + "requests" + ] + }, + "metavars": {}, + "severity": "ERROR" + }, + "path": "/test/flask/tests/e2e_zap.py", + "start": { + "col": 19, + "line": 28, + "offset": 669 + } + }, + { + "check_id": "python.requests.security.disabled-cert-validation.disabled-cert-validation", + "end": { + "col": 103, + "line": 37, + "offset": 1065 + }, + "extra": { + "is_ignored": false, + "lines": " fetch_customer_post = requests.post(\n target_url + '/fetch/customer', json=post, proxies=proxies, headers=auth_header, verify=False)", + "message": "Certificate verification has been explicitly disabled. This permits insecure connections to insecure servers. Re-enable certification validation.", + "metadata": { + "category": "security", + "cwe": "CWE-295: Improper Certificate Validation", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A3: Sensitive Data Exposure", + "references": [ + "https://stackoverflow.com/questions/41740361/is-it-safe-to-disable-ssl-certificate-verification-in-pythonss-requests-lib" + ], + "source": "https://semgrep.dev/r/python.requests.security.disabled-cert-validation.disabled-cert-validation", + "technology": [ + "requests" + ] + }, + "metavars": {}, + "severity": "ERROR" + }, + "path": "/test/flask/tests/e2e_zap.py", + "start": { + "col": 27, + "line": 36, + "offset": 948 + } + }, + { + "check_id": "python.requests.security.disabled-cert-validation.disabled-cert-validation", + "end": { + "col": 97, + "line": 45, + "offset": 1393 + }, + "extra": { + "is_ignored": false, + "lines": " search_customer_username = requests.post(\n target_url + '/search', json=search, proxies=proxies, headers=auth_header, verify=False)", + "message": "Certificate verification has been explicitly disabled. This permits insecure connections to insecure servers. Re-enable certification validation.", + "metadata": { + "category": "security", + "cwe": "CWE-295: Improper Certificate Validation", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A3: Sensitive Data Exposure", + "references": [ + "https://stackoverflow.com/questions/41740361/is-it-safe-to-disable-ssl-certificate-verification-in-pythonss-requests-lib" + ], + "source": "https://semgrep.dev/r/python.requests.security.disabled-cert-validation.disabled-cert-validation", + "technology": [ + "requests" + ] + }, + "metavars": {}, + "severity": "ERROR" + }, + "path": "/test/flask/tests/e2e_zap.py", + "start": { + "col": 32, + "line": 44, + "offset": 1282 + } + } + ] +} diff --git a/scanners/semgrep/integration-tests/semgrep.test.js b/scanners/semgrep/integration-tests/semgrep.test.js new file mode 100644 index 0000000000..333f8bcb1e --- /dev/null +++ b/scanners/semgrep/integration-tests/semgrep.test.js @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2021 iteratec GmbH +// +// SPDX-License-Identifier: Apache-2.0 + +const { scan } = require("../../../tests/integration/helpers"); + +jest.retryTimes(0); + +test( + "semgrep should find 3 issues in the test file", + async () => { + const { categories, severities, count } = await scan( + "semgrep-dummy-scan", + "semgrep", + [ + "-c", + "p/ci", + "/test/", + ], + 90, + // volumes + [{ + "name": "test-dir", + "configMap": {"name": "semgrep-test-file"} + }], + // volumeMounts + [{ + "mountPath": "/test/", + "name": "test-dir" + }], + ); + + expect(count).toBe(3); + expect(categories).toEqual({ + "security": 3, + }); + expect(severities).toEqual({ + medium: 3, + }); + }, + 3 * 60 * 1000 +); + +/* Disable this rule as it requires access to the Internet and clones a public GitHub repo +test( + "semgrep should find 9 issues in the vulnerable flask app", + async () => { + const { categories, severities, count } = await scan( + "semgrep-dummy-scan", + "semgrep", + [ + "-c", + "p/ci", + "/test/flask/", + ], + 90, + // volumes + [{ + "name": "test-dir", + "emptyDir": {} + }], + // volumeMounts + [{ + "mountPath": "/test/", + "name": "test-dir" + }], + // initContainers + [{ + "name": "init-git", + "image": "bitnami/git", + "command": ["git", "clone", "https://github.com/we45/Vulnerable-Flask-App", "/test/flask/"], + "volumeMounts": [{ + "mountPath": "/test/", + "name": "test-dir" + }] + }] + ); + + expect(count).toBe(9); + expect(categories).toEqual({ + "correctness": 1, + "security": 8, + }); + expect(severities).toEqual({ + high: 8, + medium: 1, + }); + }, + 3 * 60 * 1000 +); +*/ \ No newline at end of file diff --git a/scanners/semgrep/integration-tests/testfile.py b/scanners/semgrep/integration-tests/testfile.py new file mode 100644 index 0000000000..3bbb8ef5e3 --- /dev/null +++ b/scanners/semgrep/integration-tests/testfile.py @@ -0,0 +1,22 @@ +# Source: Example code for the semgrep rule "python.django.security.injection.command.command-injection-os-system.command-injection-os-system" +import os + +def danger(request): + # ruleid: command-injection-os-system + url = request.GET['url'] + os.system('wget ' + url) + +def danger2(request): + # ruleid: command-injection-os-system + image = request.POST['image'] + os.system("./face-recognize %s --N 24" % image) + +def danger3(request): + # ruleid: command-injection-os-system + url = request.GET['url'] + os.system("nslookup " + url) + +def ok(request): + # ok: command-injection-os-system + url = request.GET['url'] + os.system("echo 'hello'") diff --git a/scanners/semgrep/parser/Dockerfile b/scanners/semgrep/parser/Dockerfile new file mode 100644 index 0000000000..29a1b8384f --- /dev/null +++ b/scanners/semgrep/parser/Dockerfile @@ -0,0 +1,5 @@ +ARG namespace +ARG baseImageTag +FROM securecodebox/parser-sdk-nodejs:${baseImageTag:-latest} +WORKDIR /home/app/parser-wrapper/parser/ +COPY --chown=app:app ./parser.js ./parser.js \ No newline at end of file diff --git a/scanners/semgrep/parser/__testFiles__/minimal-metadata.json b/scanners/semgrep/parser/__testFiles__/minimal-metadata.json new file mode 100644 index 0000000000..6146fe339c --- /dev/null +++ b/scanners/semgrep/parser/__testFiles__/minimal-metadata.json @@ -0,0 +1,62 @@ +{ + "errors": [], + "results": [ + { + "check_id": "-", + "end": { + "col": 34, + "line": 44, + "offset": 1046 + }, + "extra": { + "is_ignored": false, + "lines": "\t\tactual := TruncateName(test.in)", + "message": "actual := TruncateName(test.in)", + "metadata": {}, + "metavars": { + "$A": { + "abstract_content": "actual", + "end": { + "col": 9, + "line": 44, + "offset": 1021 + }, + "start": { + "col": 3, + "line": 44, + "offset": 1015 + }, + "unique_id": { + "sid": 7, + "type": "id" + } + }, + "$B": { + "abstract_content": "TruncateName(test.in)", + "end": { + "col": 34, + "line": 44, + "offset": 1046 + }, + "start": { + "col": 13, + "line": 44, + "offset": 1025 + }, + "unique_id": { + "md5sum": "e3c6a62c3186d28aad5948e55d4e8a2f", + "type": "AST" + } + } + }, + "severity": "ERROR" + }, + "path": "truncatedname_test.go", + "start": { + "col": 3, + "line": 44, + "offset": 1015 + } + } + ] + } \ No newline at end of file diff --git a/scanners/semgrep/parser/__testFiles__/python-injection-multiresult.json b/scanners/semgrep/parser/__testFiles__/python-injection-multiresult.json new file mode 100644 index 0000000000..2096b73281 --- /dev/null +++ b/scanners/semgrep/parser/__testFiles__/python-injection-multiresult.json @@ -0,0 +1,211 @@ +{ + "errors": [], + "results": [ + { + "check_id": "python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + "end": { + "col": 29, + "line": 6, + "offset": 131 + }, + "extra": { + "is_ignored": false, + "lines": " url = request.GET['url']\n os.system('wget ' + url)", + "message": "Request data detected in os.system. This could be vulnerable to a command injection and should be avoided. If this must be done, use the 'subprocess' module instead and pass the arguments as a list. See https://owasp.org/www-community/attacks/Command_Injection for more information.", + "metadata": { + "category": "security", + "cwe": "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A1: Injection", + "references": [ + "https://owasp.org/www-community/attacks/Command_Injection" + ], + "source": "https://semgrep.dev/r/python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + "technology": [ + "django" + ] + }, + "metavars": { + "$DATA": { + "abstract_content": "url", + "end": { + "col": 8, + "line": 5, + "offset": 81 + }, + "start": { + "col": 5, + "line": 5, + "offset": 78 + }, + "unique_id": { + "sid": 2, + "type": "id" + } + }, + "$FUNC": { + "abstract_content": "danger", + "end": { + "col": 11, + "line": 3, + "offset": 21 + }, + "start": { + "col": 5, + "line": 3, + "offset": 15 + }, + "unique_id": { + "md5sum": "e464e589a56001f3802f41abb5379c2f", + "type": "AST" + } + }, + "$STR": { + "abstract_content": "'wget '", + "end": { + "col": 22, + "line": 6, + "offset": 124 + }, + "start": { + "col": 15, + "line": 6, + "offset": 117 + }, + "unique_id": { + "md5sum": "59ca841b827335bd089d3c8a878a2484", + "type": "AST" + } + }, + "$W": { + "abstract_content": "GET", + "end": { + "col": 22, + "line": 5, + "offset": 95 + }, + "start": { + "col": 19, + "line": 5, + "offset": 92 + }, + "unique_id": { + "md5sum": "9ed5273ba88f285705ba04822846d293", + "type": "AST" + } + } + }, + "severity": "WARNING" + }, + "path": "test.py", + "start": { + "col": 5, + "line": 5, + "offset": 78 + } + }, + { + "check_id": "python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + "end": { + "col": 52, + "line": 11, + "offset": 282 + }, + "extra": { + "is_ignored": false, + "lines": " image = request.POST['image']\n os.system(\"./face-recognize %s --N 24\" % image)", + "message": "Request data detected in os.system. This could be vulnerable to a command injection and should be avoided. If this must be done, use the 'subprocess' module instead and pass the arguments as a list. See https://owasp.org/www-community/attacks/Command_Injection for more information.", + "metadata": { + "category": "security", + "cwe": "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A1: Injection", + "references": [ + "https://owasp.org/www-community/attacks/Command_Injection" + ], + "source": "https://semgrep.dev/r/python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + "technology": [ + "django" + ] + }, + "metavars": { + "$DATA": { + "abstract_content": "image", + "end": { + "col": 10, + "line": 10, + "offset": 206 + }, + "start": { + "col": 5, + "line": 10, + "offset": 201 + }, + "unique_id": { + "sid": 4, + "type": "id" + } + }, + "$FUNC": { + "abstract_content": "danger2", + "end": { + "col": 12, + "line": 8, + "offset": 144 + }, + "start": { + "col": 5, + "line": 8, + "offset": 137 + }, + "unique_id": { + "md5sum": "f96ed4f1b3cc6e9796774e5f8b825527", + "type": "AST" + } + }, + "$STR": { + "abstract_content": "\"./face-recognize %s --N 24\"", + "end": { + "col": 43, + "line": 11, + "offset": 273 + }, + "start": { + "col": 15, + "line": 11, + "offset": 245 + }, + "unique_id": { + "md5sum": "c0d11430e9dd2fdc70d5fa03bcf2cc46", + "type": "AST" + } + }, + "$W": { + "abstract_content": "POST", + "end": { + "col": 25, + "line": 10, + "offset": 221 + }, + "start": { + "col": 21, + "line": 10, + "offset": 217 + }, + "unique_id": { + "md5sum": "2976ab2fedc9ac86ab8ba5a7e10329ac", + "type": "AST" + } + } + }, + "severity": "WARNING" + }, + "path": "test.py", + "start": { + "col": 5, + "line": 10, + "offset": 201 + } + } + ] + } \ No newline at end of file diff --git a/scanners/semgrep/parser/__testFiles__/python-injection.json b/scanners/semgrep/parser/__testFiles__/python-injection.json new file mode 100644 index 0000000000..e63928d688 --- /dev/null +++ b/scanners/semgrep/parser/__testFiles__/python-injection.json @@ -0,0 +1,108 @@ +{ + "errors": [], + "results": [ + { + "check_id": "python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + "end": { + "col": 29, + "line": 5, + "offset": 89 + }, + "extra": { + "is_ignored": false, + "lines": " url = request.GET['url']\n os.system('wget ' + url)", + "message": "Request data detected in os.system. This could be vulnerable to a command injection and should be avoided. If this must be done, use the 'subprocess' module instead and pass the arguments as a list. See https://owasp.org/www-community/attacks/Command_Injection for more information.", + "metadata": { + "category": "security", + "cwe": "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')", + "license": "Commons Clause License Condition v1.0[LGPL-2.1-only]", + "owasp": "A1: Injection", + "references": [ + "https://owasp.org/www-community/attacks/Command_Injection" + ], + "source": "https://semgrep.dev/r/python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + "technology": [ + "django" + ] + }, + "metavars": { + "$DATA": { + "abstract_content": "url", + "end": { + "col": 8, + "line": 4, + "offset": 39 + }, + "start": { + "col": 5, + "line": 4, + "offset": 36 + }, + "unique_id": { + "sid": 2, + "type": "id" + } + }, + "$FUNC": { + "abstract_content": "danger", + "end": { + "col": 11, + "line": 3, + "offset": 21 + }, + "start": { + "col": 5, + "line": 3, + "offset": 15 + }, + "unique_id": { + "md5sum": "e464e589a56001f3802f41abb5379c2f", + "type": "AST" + } + }, + "$STR": { + "abstract_content": "'wget '", + "end": { + "col": 22, + "line": 5, + "offset": 82 + }, + "start": { + "col": 15, + "line": 5, + "offset": 75 + }, + "unique_id": { + "md5sum": "59ca841b827335bd089d3c8a878a2484", + "type": "AST" + } + }, + "$W": { + "abstract_content": "GET", + "end": { + "col": 22, + "line": 4, + "offset": 53 + }, + "start": { + "col": 19, + "line": 4, + "offset": 50 + }, + "unique_id": { + "md5sum": "9ed5273ba88f285705ba04822846d293", + "type": "AST" + } + } + }, + "severity": "WARNING" + }, + "path": "test.py", + "start": { + "col": 5, + "line": 4, + "offset": 36 + } + } + ] +} diff --git a/scanners/semgrep/parser/package-lock.json b/scanners/semgrep/parser/package-lock.json new file mode 100644 index 0000000000..06a578b055 --- /dev/null +++ b/scanners/semgrep/parser/package-lock.json @@ -0,0 +1,74 @@ +{ + "name": "semgrep-parser", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "semgrep-parser", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.20", + "xml2js": "^0.4.23" + }, + "devDependencies": {} + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + } + }, + "dependencies": { + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + } + } +} diff --git a/scanners/semgrep/parser/package.json b/scanners/semgrep/parser/package.json new file mode 100644 index 0000000000..c310b338fa --- /dev/null +++ b/scanners/semgrep/parser/package.json @@ -0,0 +1,15 @@ +{ + "name": "@securecodebox/semgrep-parser", + "version": "1.0.0", + "description": "Parses result files for the type: 'semgrep-json'", + "main": "", + "scripts": {}, + "keywords": [], + "author": "iteratec GmbH", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.20", + "xml2js": "^0.4.23" + }, + "devDependencies": {} +} \ No newline at end of file diff --git a/scanners/semgrep/parser/parser.js b/scanners/semgrep/parser/parser.js new file mode 100644 index 0000000000..a0454f36fe --- /dev/null +++ b/scanners/semgrep/parser/parser.js @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2021 iteratec GmbH +// +// SPDX-License-Identifier: Apache-2.0 + +const severityMap = new Map([ + ["info", "INFORMATIONAL"], + ["warning", "MEDIUM"], + ["error", "HIGH"] +]) +async function parse(fileContent) { + const results = fileContent + return results.results.flatMap(result => { + // Assemble location as path to file and line range + const location = result.path + ":" + result.start.line + "-" + result.end.line; + + // Name of the finding is the rule ID from semgrep + const name = result.check_id + + // Description is either the message from result.extra.message, or a placeholder message + const description = result.extra.message || "(No description provided in semgrep rule - when using a custom rule, set the 'message' key)" + + // Category of the finding - use either result.extra.metadata.category, or a placeholder + const category = result.extra.metadata.category || "semgrep-result" + + // severity of the issue: translate semgrep severity levels (INFO, WARNING, ERROR) to those of SCB (INFORMATIONAL, LOW, MEDIUM, HIGH) + const severity = severityMap.has(result.extra.severity.toLowerCase()) ? severityMap.get(result.extra.severity.toLowerCase()) : "INFORMATIONAL" + + const attributes = { + // Common weakness enumeration, if available + "cwe": result.extra.metadata.cwe || null, + // OWASP category, if available + "owasp_category": result.extra.metadata.owasp || null, + // References given in the rule + "references": result.extra.metadata.references || null, + // Link to the semgrep rule + "rule_source": result.extra.metadata.source || null, + // Which line of code matched? + // TODO: Do we actually want to record this? There are also secret-detector rules for semgrep, + // so maybe you don't actually want the plaintext match to be recorded unencrypted in some S3 bucket? + // "matching_lines": result.extra.lines, + } + + return { + "name": name, + "location": location, + "description": description, + "category": category, + "severity": severity, + "attributes": attributes + } + }) +} + +module.exports.parse = parse; diff --git a/scanners/semgrep/parser/parser.test.js b/scanners/semgrep/parser/parser.test.js new file mode 100644 index 0000000000..d340f8298b --- /dev/null +++ b/scanners/semgrep/parser/parser.test.js @@ -0,0 +1,116 @@ +const fs = require("fs"); +const util = require("util"); +const { + validateParser, +} = require("@securecodebox/parser-sdk-nodejs/parser-utils"); + +const { parse } = require("./parser"); + +// eslint-disable-next-line security/detect-non-literal-fs-filename +const readFile = util.promisify(fs.readFile); + +test("should properly parse file from inline semgrep usage", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/minimal-metadata.json", + { + encoding: "utf8", + } + ); + const findings = await parse(JSON.parse(jsonContent)); + // validate findings + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot(` + Array [ + Object { + "attributes": Object { + "cwe": null, + "owasp_category": null, + "references": null, + "rule_source": null, + }, + "category": "semgrep-result", + "description": "actual := TruncateName(test.in)", + "location": "truncatedname_test.go:44-44", + "name": "-", + "severity": "HIGH", + }, + ] + `); +}); + +test("should properly parse file with a single result", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/python-injection.json", + { + encoding: "utf8", + } + ); + const findings = await parse(JSON.parse(jsonContent)); + // validate findings + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot(` +Array [ + Object { + "attributes": Object { + "cwe": "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')", + "owasp_category": "A1: Injection", + "references": Array [ + "https://owasp.org/www-community/attacks/Command_Injection", + ], + "rule_source": "https://semgrep.dev/r/python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + }, + "category": "security", + "description": "Request data detected in os.system. This could be vulnerable to a command injection and should be avoided. If this must be done, use the 'subprocess' module instead and pass the arguments as a list. See https://owasp.org/www-community/attacks/Command_Injection for more information.", + "location": "test.py:4-5", + "name": "python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + "severity": "MEDIUM", + }, +] +`); +}); + +test("should properly parse file with multiple results", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/python-injection-multiresult.json", + { + encoding: "utf8", + } + ); + const findings = await parse(JSON.parse(jsonContent)); + // validate findings + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot(` +Array [ + Object { + "attributes": Object { + "cwe": "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')", + "owasp_category": "A1: Injection", + "references": Array [ + "https://owasp.org/www-community/attacks/Command_Injection", + ], + "rule_source": "https://semgrep.dev/r/python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + }, + "category": "security", + "description": "Request data detected in os.system. This could be vulnerable to a command injection and should be avoided. If this must be done, use the 'subprocess' module instead and pass the arguments as a list. See https://owasp.org/www-community/attacks/Command_Injection for more information.", + "location": "test.py:5-6", + "name": "python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + "severity": "MEDIUM", + }, + Object { + "attributes": Object { + "cwe": "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')", + "owasp_category": "A1: Injection", + "references": Array [ + "https://owasp.org/www-community/attacks/Command_Injection", + ], + "rule_source": "https://semgrep.dev/r/python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + }, + "category": "security", + "description": "Request data detected in os.system. This could be vulnerable to a command injection and should be avoided. If this must be done, use the 'subprocess' module instead and pass the arguments as a list. See https://owasp.org/www-community/attacks/Command_Injection for more information.", + "location": "test.py:10-11", + "name": "python.django.security.injection.command.command-injection-os-system.command-injection-os-system", + "severity": "MEDIUM", + }, +] +`); +}); diff --git a/scanners/semgrep/templates/cascading-rules.yaml b/scanners/semgrep/templates/cascading-rules.yaml new file mode 100644 index 0000000000..97dd683b66 --- /dev/null +++ b/scanners/semgrep/templates/cascading-rules.yaml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +# We only want to import the default cascading rules if they are enabled +{{ if .Values.cascadingRules.enabled }} +# The CascadingRules are not directly in the /templates directory as their curly bracket syntax clashes with helms templates ... :( +# We import them as raw files to avoid these clashes as escaping them is even more messy +{{ range $path, $_ := .Files.Glob "cascading-rules/*" }} +# Include File +{{ $.Files.Get $path }} +# Separate multiple files +--- +{{ end }} +{{ end }} diff --git a/scanners/semgrep/templates/semgrep-parse-definition.yaml b/scanners/semgrep/templates/semgrep-parse-definition.yaml new file mode 100644 index 0000000000..e45c12a23f --- /dev/null +++ b/scanners/semgrep/templates/semgrep-parse-definition.yaml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: "execution.securecodebox.io/v1" +kind: ParseDefinition +metadata: + name: "semgrep-json" +spec: + image: "{{ .Values.parser.image.repository }}:{{ .Values.parser.image.tag | default .Chart.Version }}" + imagePullPolicy: {{ .Values.parser.image.pullPolicy }} + ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} + env: + {{- toYaml .Values.parser.env | nindent 4 }} diff --git a/scanners/semgrep/templates/semgrep-scan-type.yaml b/scanners/semgrep/templates/semgrep-scan-type.yaml new file mode 100644 index 0000000000..a91379cc7b --- /dev/null +++ b/scanners/semgrep/templates/semgrep-scan-type.yaml @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: 2021 iteratec GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: "execution.securecodebox.io/v1" +kind: ScanType +metadata: + name: "semgrep{{ .Values.scanner.nameAppend | default ""}}" +spec: + extractResults: + type: semgrep-json + location: "/home/securecodebox/semgrep-results.json" + jobTemplate: + spec: + {{- if .Values.scanner.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ .Values.scanner.ttlSecondsAfterFinished }} + {{- end }} + backoffLimit: {{ .Values.scanner.backoffLimit }} + {{- if .Values.scanner.activeDeadlineSeconds }} + activeDeadlineSeconds: {{ .Values.scanner.activeDeadlineSeconds }} + {{- end }} + template: + spec: + restartPolicy: OnFailure + containers: + - name: semgrep + image: "{{ .Values.scanner.image.repository }}:{{ .Values.scanner.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.scanner.image.pullPolicy }} + command: ["semgrep", "--json", "-o", "/home/securecodebox/semgrep-results.json"] + resources: + {{- toYaml .Values.scanner.resources | nindent 16 }} + securityContext: + {{- toYaml .Values.scanner.securityContext | nindent 16 }} + env: + {{- toYaml .Values.scanner.env | nindent 16 }} + volumeMounts: + {{- toYaml .Values.scanner.extraVolumeMounts | nindent 16 }} + {{- if .Values.scanner.extraContainers }} + {{- toYaml .Values.scanner.extraContainers | nindent 12 }} + {{- end }} + volumes: + {{- toYaml .Values.scanner.extraVolumes | nindent 12 }} diff --git a/scanners/semgrep/values.yaml b/scanners/semgrep/values.yaml new file mode 100644 index 0000000000..8468a4311f --- /dev/null +++ b/scanners/semgrep/values.yaml @@ -0,0 +1,49 @@ +parser: + image: + repository: securecodebox/parser-semgrep + tag: null + pullPolicy: IfNotPresent + + backoffLimit: 3 + env: [] + + +scanner: + image: + repository: docker.io/returntocorp/semgrep + tag: null + pullPolicy: IfNotPresent + + ttlSecondsAfterFinished: null + + backoffLimit: 3 + + resources: {} + # resources: + # requests: + # memory: "256Mi" + # cpu: "250m" + # limits: + # memory: "512Mi" + # cpu: "500m" + + env: [] + + extraVolumes: [] + + extraVolumeMounts: [] + + extraContainers: [] + + securityContext: + runAsNonRoot: true + readOnlyRootFilesystem: false + allowPrivilegeEscalation: false + privileged: false + capabilities: + drop: + - all + +cascadingRules: + # cascadingRules.enabled -- Enables or disables the installation of the default cascading rules for this scanner + enabled: true \ No newline at end of file diff --git a/tests/integration/helpers.js b/tests/integration/helpers.js index 10028f3285..660289cfc3 100644 --- a/tests/integration/helpers.js +++ b/tests/integration/helpers.js @@ -110,9 +110,12 @@ async function disasterRecovery(scanName) { * @param {string} scanType type of the scan. Must match the name of a ScanType CRD * @param {string[]} parameters cli argument to be passed to the scanner * @param {number} timeout in seconds + * @param {object[]} volumes definitions for kubernetes volumes that should be used. Optional, useful for initContainers (see below) + * @param {object[]} volumeMounts definitions for kubernetes volume mounts that should be used. Optional, useful for initContainers (see below) + * @param {object[]} initContainers definitions for initContainers that should be added to the scan job to provision files for the scanner. Optional. * @returns {scan.findings} returns findings { categories, severities, count } */ -async function scan(name, scanType, parameters = [], timeout = 180) { +async function scan(name, scanType, parameters = [], timeout = 180, volumes = [], volumeMounts = [], initContainers = []) { namespace ="integration-tests" const scanDefinition = { apiVersion: "execution.securecodebox.io/v1", @@ -124,6 +127,9 @@ async function scan(name, scanType, parameters = [], timeout = 180) { spec: { scanType, parameters, + volumes, + volumeMounts, + initContainers, }, };