Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 47 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ project settings CircleCI. Then include the following in your `.circleci/config.
version: 2.1
setup: true
orbs:
build: mojaloop/build@1.1.1
build: mojaloop/build@1.1.2
workflows:
setup:
jobs:
Expand Down Expand Up @@ -50,13 +50,23 @@ workflows:
# publish_docker_prerelease_resource_class: medium
```

### Vulnerability Image Scan Configuration
### Vulnerability Scan Configuration

The repo using the orb, must declare a .grype.yaml file in the root of the repo.
As necessary vulnerabilities can be ignored per following example:
The repo using the orb must declare a `.grype.yaml` file in the root of the repo. The orb includes both image and source scan jobs, but only the appropriate one will execute based on your configuration.

#### Scan Type Configuration

You can explicitly specify the scan type in your `.grype.yaml`:

```yaml
# Set to true to disable the Grype image scan completely
# Specify the type of scan (optional)
# Values: "image" for Docker image scan, "source" for source code scan
# If not specified, the orb will auto-detect:
# - If Dockerfile exists: runs image scan
# - If no Dockerfile exists: runs source scan
scan-type: source # or "image"

# Set to true to disable Grype scanning completely
disabled: false

ignore:
Expand All @@ -72,14 +82,44 @@ output:
- "table"
- "json"

# Modify your CircleCI job to check critical count
# For image scans - modify scope
search:
scope: "squashed"
quiet: false
check-for-app-update: false
```

To completely disable the Grype image scan, set `disabled: true` in your `.grype.yaml` file. This will cause the scan job to exit successfully without performing any checks.
#### How It Works

The workflow includes both `grype_image_scan` and `grype_source_scan` jobs. Each job checks your configuration and only runs when appropriate:

- **`grype_image_scan`**: Executes when `scan-type: image` is set or a Dockerfile exists
- **`grype_source_scan`**: Executes when `scan-type: source` is set or no Dockerfile exists
- Only one scan type will run per build

#### Scan Types

1. **Docker Image Scan** (`scan-type: image`):
- Used for repositories that build Docker images
- Scans the built Docker image for vulnerabilities
- Requires a Dockerfile in the repository
- The image must be built in the Build job
- Runs after the Build job completes

2. **Source Code Scan** (`scan-type: source`):
- Used for library repositories without Docker images
- Scans the source code and dependencies directly
- Analyzes package.json, package-lock.json, and other dependency files
- No Docker image required
- Runs after the Setup job completes

#### Auto-detection

If `scan-type` is not specified in `.grype.yaml`, the orb will automatically determine which scan to run:
- If a `Dockerfile` exists in the repository root → `grype_image_scan` runs
- If no `Dockerfile` exists → `grype_source_scan` runs

To completely disable Grype scanning, set `disabled: true` in your `.grype.yaml` file. This will cause both scan jobs to skip successfully.

### Additional Workflows

Expand Down
2 changes: 1 addition & 1 deletion src/examples/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ usage:
version: 2.1
setup: true
orbs:
build: mojaloop/build@1.1.1
build: mojaloop/build@1.1.2
workflows:
setup:
jobs:
Expand Down
24 changes: 22 additions & 2 deletions src/jobs/grype_image_scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ steps:
- attach_workspace:
at: /tmp
- run:
name: Check for Grype config
name: Check for Grype config and scan type
command: |
if [ ! -f .grype.yaml ]; then
echo "Error: .grype.yaml configuration file not found in repository at root level."
Expand All @@ -30,7 +30,27 @@ steps:
if grep -q "disabled: true" .grype.yaml; then
echo "Grype image scan is disabled in .grype.yaml"
circleci-agent step halt
echo "This should not be printed"
fi

# Determine if image scan should run
SHOULD_RUN_IMAGE="false"

# Check explicit scan-type configuration
if grep -q "scan-type: image" .grype.yaml; then
SHOULD_RUN_IMAGE="true"
echo "scan-type: image found in .grype.yaml - running image scan"
elif grep -q "scan-type: source" .grype.yaml; then
echo "scan-type: source found in .grype.yaml - skipping image scan"
circleci-agent step halt
else
# Auto-detect based on Dockerfile presence
if [ -f Dockerfile ]; then
SHOULD_RUN_IMAGE="true"
echo "Dockerfile found and no scan-type specified - running image scan"
else
echo "No Dockerfile found and no scan-type specified - skipping image scan (source scan will run)"
circleci-agent step halt
fi
fi
- run:
name: Check dependencies
Expand Down
105 changes: 105 additions & 0 deletions src/jobs/grype_source_scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
description: |
This job scans the source code for vulnerabilities using Grype.
executor:
name: machine
resource_class: << parameters.resource_class >>
environment:
MAIN_BRANCH_NAME: main
BASH_ENV: /etc/profile ## Ref: https://circleci.com/docs/env-vars/#alpine-linux
ENV: ~/.profile
NVM_ARCH_UNOFFICIAL_OVERRIDE: x64-musl ## Ref: https://github.com/nvm-sh/nvm/issues/1102#issuecomment-550572252
parameters:
resource_class:
type: enum
enum: ["small", "medium", "medium+", "large", "xlarge", "2xlarge", "2xlarge+"]
default: medium
steps:
- checkout
- run:
name: Check for Grype config and scan type
command: |
if [ ! -f .grype.yaml ]; then
echo "Error: .grype.yaml configuration file not found in repository at root level."
echo "Please add a .grype.yaml file with appropriate vulnerability ignore rules."
exit 1
fi

# Check if scan is disabled
if grep -q "disabled: true" .grype.yaml; then
echo "Grype source scan is disabled in .grype.yaml"
circleci-agent step halt
fi

# Determine if source scan should run
SHOULD_RUN_SOURCE="false"

# Check explicit scan-type configuration
if grep -q "scan-type: source" .grype.yaml; then
SHOULD_RUN_SOURCE="true"
echo "scan-type: source found in .grype.yaml - running source scan"
elif grep -q "scan-type: image" .grype.yaml; then
echo "scan-type: image found in .grype.yaml - skipping source scan"
circleci-agent step halt
else
# Auto-detect based on Dockerfile presence
if [ ! -f Dockerfile ]; then
SHOULD_RUN_SOURCE="true"
echo "No Dockerfile found and no scan-type specified - running source scan"
else
echo "Dockerfile found and no scan-type specified - skipping source scan (image scan will run)"
circleci-agent step halt
fi
fi
- run:
name: Check dependencies
command: |
if ! command -v jq &> /dev/null; then
echo "jq could not be found, installing..."
sudo apt-get update && sudo apt-get install -y jq
fi
- run:
name: Install Grype
command: |
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sudo sh -s -- -b /usr/local/bin
- run:
name: Run Grype source code scan with custom config
command: |
echo "Scanning source code in current directory"
# Use the config file in your repo to scan the source code
grype dir:. -c .grype.yaml -o table > grype-results.txt
grype dir:. -c .grype.yaml -o json > grype-results.json
cat grype-results.txt
cat grype-results.json
- run:
name: Check for critical, high and medium vulnerabilities
command: |
# Count vulnerabilities in the filtered results
CRITICAL_COUNT=$(cat grype-results.json | jq '[.matches[] | select(.vulnerability.severity == "Critical")] | length')
HIGH_COUNT=$(cat grype-results.json | jq '[.matches[] | select(.vulnerability.severity == "High")] | length')
MEDIUM_COUNT=$(cat grype-results.json | jq '[.matches[] | select(.vulnerability.severity == "Medium")] | length')

echo "Critical vulnerabilities found: $CRITICAL_COUNT"
echo "High vulnerabilities found: $HIGH_COUNT"
echo "Medium vulnerabilities found: $MEDIUM_COUNT"

# List remaining critical, high and medium vulnerabilities for awareness
echo "Critical severity vulnerabilities:"
cat grype-results.json | jq -r '.matches[] | select(.vulnerability.severity == "Critical") | "- \(.artifact.name) \(.artifact.version): \(.vulnerability.id)"'

echo "High severity vulnerabilities:"
cat grype-results.json | jq -r '.matches[] | select(.vulnerability.severity == "High") | "- \(.artifact.name) \(.artifact.version): \(.vulnerability.id)"'

echo "Medium severity vulnerabilities:"
cat grype-results.json | jq -r '.matches[] | select(.vulnerability.severity == "Medium") | "- \(.artifact.name) \(.artifact.version): \(.vulnerability.id)"'

# Fail if any critical, high, or medium vulnerabilities are found
if [ "$CRITICAL_COUNT" -gt 0 ] || [ "$HIGH_COUNT" -gt 0 ] || [ "$MEDIUM_COUNT" -gt 0 ]; then
echo "Critical, High, or Medium vulnerabilities found. Failing the build."
exit 1
fi
- store_artifacts:
path: grype-results.json
destination: grype-scan/scan-results.json
- store_artifacts:
path: grype-results.txt
destination: grype-scan/scan-results.txt
4 changes: 2 additions & 2 deletions src/jobs/test_deprecations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ steps:
name: Execute deprecations tests
command: |
if [ -f yarn.lock ]; then
npx -y --package @mojaloop/ml-depcheck-utility@1.1.1 check-deprecations-yarn
npx -y --package @mojaloop/ml-depcheck-utility@1.1.2 check-deprecations-yarn
Comment thread
gibaros marked this conversation as resolved.
Outdated
else
npx -y --package @mojaloop/ml-depcheck-utility@1.1.1 check-deprecations-npm
npx -y --package @mojaloop/ml-depcheck-utility@1.1.2 check-deprecations-npm
Comment thread
gibaros marked this conversation as resolved.
Outdated
fi
18 changes: 15 additions & 3 deletions src/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: 2.1
orbs:
build: mojaloop/build@1.1.1
build: mojaloop/build@1.1.2
parameters:
git_tag:
type: string
Expand Down Expand Up @@ -188,7 +188,7 @@ workflows:
<<: *test-docker
name: Functional tests
resource_class: << pipeline.parameters.test_functional_resource_class >>
- build/license_scan: &scan
- build/license_scan:
name: License scan
resource_class: << pipeline.parameters.license_scan_resource_class >>
context: org-global
Expand All @@ -199,7 +199,7 @@ workflows:
tags: *setup-tags
requires:
- Build
- build/grype_image_scan: &grype_image_scan
- build/grype_image_scan:
name: Grype image scan
resource_class: << pipeline.parameters.grype_image_scan_resource_class >>
context: org-global
Expand All @@ -209,6 +209,16 @@ workflows:
tags: *setup-tags
requires:
- Build
- build/grype_source_scan:
name: Grype source scan
resource_class: << pipeline.parameters.grype_image_scan_resource_class >>
context: org-global
filters:
branches:
only: /.*/
tags: *setup-tags
requires:
- Setup
- build/release:
name: Release
resource_class: << pipeline.parameters.release_resource_class >>
Expand All @@ -231,6 +241,7 @@ workflows:
- License audit
- License scan
- Grype image scan
- Grype source scan
- build/github_release:
name: GitHub release
resource_class: << pipeline.parameters.github_release_resource_class >>
Expand Down Expand Up @@ -293,6 +304,7 @@ workflows:
- License audit
- License scan
- Grype image scan
- Grype source scan
- build/publish_docker_prerelease:
name: Docker prerelease
resource_class: << pipeline.parameters.publish_docker_prerelease_resource_class >>
Expand Down