Skip to content

Commit

Permalink
initial safety action
Browse files Browse the repository at this point in the history
  • Loading branch information
yeisonvargasf committed Jan 18, 2024
0 parents commit d3575b1
Show file tree
Hide file tree
Showing 16 changed files with 471 additions and 0 deletions.
147 changes: 147 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
name: Safety Action CI

on:
push:

jobs:
matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- id: set-matrix
run: |
TASKS=$(echo $(cat .github/workflows/gh-action-integration-matrix.json) | sed 's/ //g' )
echo "matrix=$TASKS" >> $GITHUB_OUTPUT
test-requirements-txt-insecure:
needs: [ matrix ]
runs-on: ubuntu-latest
environment: main
strategy:
matrix:
action: ${{ fromJson(needs.matrix.outputs.matrix) }}

steps:
- uses: actions/checkout@v4
with:
ref: ${{ matrix.action.version }}

- run: cp tests/requirements.txt-insecure requirements.txt

- uses: ./
id: scan-1
with:
api-key: ${{ secrets.SAFETY_API_KEY }}

- if: steps.scan-1.outcome == 'failure' && steps.scan-1.outputs.exit-code == '64'
run: exit 1

# Same as above, but for a poetry lock file
test-poetry-insecure:
needs: [ matrix ]
runs-on: ubuntu-latest
environment: main
strategy:
matrix:
action: ${{ fromJson(needs.matrix.outputs.matrix) }}

steps:
- uses: actions/checkout@v4
with:
ref: ${{ matrix.action.version }}

- run: cp tests/poetry.lock-insecure poetry.lock && cp tests/pyproject.toml-insecure pyproject.toml

- uses: ./
id: scan-2
with:
api-key: ${{ secrets.SAFETY_API_KEY }}

- if: steps.scan-2.outcome == 'failure' && steps.scan-2.outputs.exit-code == '64'
run: exit 1

test-pipfile-insecure:
needs: [ matrix ]
runs-on: ubuntu-latest
environment: main
strategy:
matrix:
action: ${{ fromJson(needs.matrix.outputs.matrix) }}

steps:
- uses: actions/checkout@v4
with:
ref: ${{ matrix.action.version }}

- run: cp tests/Pipfile.lock-insecure Pipfile.lock

- uses: ./
id: scan-3
with:
api-key: ${{ secrets.SAFETY_API_KEY }}

- if: steps.scan-3.outcome == 'failure' && steps.scan-3.outputs.exit-code == '64'
run: exit 1

test-requirements-txt-secure:
needs: [ matrix ]
runs-on: ubuntu-latest
environment: main
strategy:
matrix:
action: ${{ fromJson(needs.matrix.outputs.matrix) }}

steps:
- uses: actions/checkout@v4
with:
ref: ${{ matrix.action.version }}

- run: cp tests/requirements.txt-secure requirements.txt

- uses: ./
id: scan-4
with:
api-key: ${{ secrets.SAFETY_API_KEY }}

test-poetry-secure:
needs: [ matrix ]
runs-on: ubuntu-latest
environment: main
strategy:
matrix:
action: ${{ fromJson(needs.matrix.outputs.matrix) }}

steps:
- uses: actions/checkout@v4
with:
ref: ${{ matrix.action.version }}

- run: cp tests/poetry.lock-secure poetry.lock && cp tests/pyproject.toml-secure pyproject.toml

- uses: ./
id: scan-5
with:
api-key: ${{ secrets.SAFETY_API_KEY }}

# Same as above, but for a Pipfile.lock
test-pipfile-secure:
needs: [ matrix ]
runs-on: ubuntu-latest
environment: main
strategy:
matrix:
action: ${{ fromJson(needs.matrix.outputs.matrix) }}

steps:
- uses: actions/checkout@v4
with:
ref: ${{ matrix.action.version }}

- run: cp tests/Pipfile.lock-secure Pipfile.lock

- uses: ./
id: scan-6
with:
api-key: ${{ secrets.SAFETY_API_KEY }}
3 changes: 3 additions & 0 deletions .github/workflows/gh-action-integration-matrix.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
{"version": ""}
]
32 changes: 32 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Update Main Version
run-name: Move ${{ github.event.inputs.major_version }} to ${{ github.event.inputs.target }}

on:
workflow_dispatch:
inputs:
target:
description: The tag or reference to use
required: true
major_version:
type: choice
description: The major version to update
options:
- v1
- v2
- v3

jobs:
tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Git config
run: |
git config user.name github-actions
git config user.email [email protected]
- name: Tag new target
run: git tag -f ${{ github.event.inputs.major_version }} ${{ github.event.inputs.target }}
- name: Push new tag
run: git push origin ${{ github.event.inputs.major_version }} --force
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is partly based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## [1.0.0] - 2024-01-18
- basic support for the scan command
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM ghcr.io/pyupio/safety:3.0.0-615ef36

COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh

ENTRYPOINT ["/app/entrypoint.sh"]
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Using Safety as a GitHub Action

Safety can be integrated into your existing GitHub CI pipeline as an action. Just add the following as a step in your workflow YAML file after setting your `SAFETY_API_KEY` secret on GitHub under Settings -> Secrets -> Actions:

```yaml
- uses: pyupio/safety-action@v1
with:
api-key: ${{ secrets.SAFETY_API_KEY }}
```
(Don't have an API Key? You can sign up for one with [https://safetycli.com/resources/plans](https://safetycli.com/resources/plans).)
This will run Safety scan and It'll fail your CI pipeline if any vulnerable packages are found.
If you have something more complicated such as a monorepo; or once you're finished testing, read the [Documentation](https://docs.safetycli.com/) for more details on configuring Safety as an action.
46 changes: 46 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: 'pyupio/safety-action'
description: 'Runs the Safety CLI dependency scanner against your project'
inputs:
api-key:
description: 'Safety CLI API key'
required: true
default: ''
output-format:
description: 'Output format for returned data. One of screen / json / html / spdx (defaults to screen)'
required: false
default: 'screen'
args:
description: '[Advanced] Any additional arguments to pass to Safety'
required: false
default: ''
repo-token:
required: false
default: ''
debug:
required: false
default: false

outputs:
cli-output:
description: 'CLI output from Safety'
exit-code:
description: 'Exit code from Safety'

runs:
using: "docker"
image: "Dockerfile"
env:
SAFETY_API_KEY: ${{ inputs.api-key }}
SAFETY_DEBUG: ${{ inputs.debug }}
SAFETY_ACTION_OUTPUT_FORMAT: ${{ inputs.output-format }}
SAFETY_ACTION_ARGS: ${{ inputs.args }}
GITHUB_TOKEN: ${{ inputs.repo-token }}
SAFETY_ACTION_VERSION: 1.0.0
SAFETY_ACTION: true
COLUMNS: 120
FORCE_COLOR: 1
NON_INTERACTIVE: 1

branding:
icon: 'lock'
color: 'purple'
87 changes: 87 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env bash
set -eu -o pipefail

# Early out if the API key is not set
if [[ "${SAFETY_API_KEY:-}" == "" ]]; then
echo "[Safety Action] An API key is required to use this action. Please sign up for an account at https://safetycli.com/" 1>&2
exit 1
fi

export SAFETY_OS_TYPE="docker action"
export SAFETY_OS_RELEASE=""
export SAFETY_OS_DESCRIPTION=""

if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then
SAFETY_GIT_BRANCH=$(jq --raw-output .pull_request.head.ref "$GITHUB_EVENT_PATH")
SAFETY_GIT_COMMIT=$(jq -r ".pull_request.head.sha" $GITHUB_EVENT_PATH)
else
SAFETY_GIT_BRANCH=${GITHUB_REF#refs/heads/}
SAFETY_GIT_COMMIT=$GITHUB_SHA
fi

if [[ $GITHUB_REF == refs/tags/* ]]; then
SAFETY_GIT_TAG=${GITHUB_REF#refs/tags/}
else
SAFETY_GIT_TAG=""
fi

# Set up Git configuration to enable certain git commands within the GitHub Actions environment
git config --system --add safe.directory /github/workspace

# Check if debug mode is enabled
if [[ "${SAFETY_DEBUG:-}" == "true" ]]; then
echo "Action running in debug mode, debug info"
echo "-----------------------------------------"

echo "GitHub Action debug information:"
echo "GITHUB_EVENT_NAME: $GITHUB_EVENT_NAME"
echo "GITHUB_REF: $GITHUB_REF"
echo "Git remote origin: $(git remote get-url origin)"
echo "Git branch: $SAFETY_GIT_BRANCH"
echo "Git commit: $SAFETY_GIT_COMMIT"
if [[ $GITHUB_REF == refs/tags/* ]]; then
echo "Git tag: $SAFETY_GIT_TAG"
else
echo "Git tag: not a tag event"
fi
echo "-----------------------------------------"

# Set the debug argument for the Safety CLI
echo "Running \"safety scan\" in debug mode:"
SAFETY_DEBUG_ARG="--debug"
else
SAFETY_DEBUG_ARG=""
fi

echo "Safety Action version: $SAFETY_ACTION_VERSION"
echo "Safety CLI version: $(python -m safety --version)"

# Export environment variables for use by the Safety CLI
export SAFETY_GIT_BRANCH=$SAFETY_GIT_BRANCH
export SAFETY_GIT_COMMIT=$SAFETY_GIT_COMMIT
export SAFETY_GIT_TAG=$SAFETY_GIT_TAG

# Don't hard fail from here on out; so we can return the exit code and output
set +e

# Process the output of Safety CLI for proper display in GitHub Actions
# This involves removing special characters and encoding multi-line strings
# This also sends the output to both stdout and our variable, without buffering like echo would.
# sed -E ':a;N;$!ba;s/\n{3,}/\n\n/g' replace any occurrence of three or more newlines (\n{3,}) with exactly two newlines (\n\n)
# sed -E 's/\x1b\[\??25[lh]//g' remove certain ANSI escape codes related to cursor control (like hiding/showing the cursor) from the output of a command
exec 5>&1
output=$(python -m safety ${SAFETY_DEBUG_ARG} --stage cicd scan --output="${SAFETY_ACTION_OUTPUT_FORMAT}" ${SAFETY_ACTION_ARGS} | stdbuf -o0 sed -E 's/\x1b\[\??25[lh]//g' | sed -E ':a;N;$!ba;s/\n{3,}/\n\n/g' | tee >(cat - >&5))
exit_code=$?

# https://github.community/t/set-output-truncates-multiline-strings/16852/3
# Encoding for GitHub Actions to handle multi-line strings and special characters
output="${output//'%'/'%25'}" # Replace % with %25
output="${output//$'\n'/'%0A'}" # Replace newline characters with %0A
output="${output//$'\r'/'%0D'}" # Replace carriage return characters with %0D

# Output the exit code and CLI output for GitHub Actions to consume
echo "{exit-code}={$exit_code}" >> $GITHUB_OUTPUT
echo "{cli-output}={$output}" >> $GITHUB_OUTPUT

# Exit with the same code as the Safety CLI command
exit $exit_code
29 changes: 29 additions & 0 deletions tests/Pipfile.lock-insecure

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d3575b1

Please sign in to comment.