Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Safety 2.3.0 patch #415

Merged
merged 10 commits into from Oct 5, 2022
2 changes: 1 addition & 1 deletion Dockerfile
Expand Up @@ -10,7 +10,7 @@ RUN cd /app && python3 -m pip install poetry==1.1.13 pipenv==2022.6.7

# Install this project dependencies
COPY . /app
RUN cd /app && python3 -m pip install -e .
RUN cd /app && python3 -m pip install -e .[github]

ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
Expand Down
14 changes: 14 additions & 0 deletions action.yml
Expand Up @@ -6,6 +6,14 @@ inputs:
description: 'PyUp.io API key'
required: false
default: ''
create-pr:
description: 'Create a PR if Safety finds any vulnerabilities. Only the `file` scan mode is currently supported - combine with Cron based actions to get automatic notifications of new vulnerabilities.'
required: false
default: ''
create-issue:
description: 'Create an issue if Safety finds any vulnerabilities. Only the `file` scan mode is currently supported - combine with Cron based actions to get automatic notifications of new vulnerabilities.'
required: false
default: ''
scan:
description: 'Scan mode to use. One of auto / docker / env / file (defaults to auto)'
required: false
Expand All @@ -30,6 +38,9 @@ inputs:
description: '[Advanced] Any additional arguments to pass to Safety'
required: false
default: ''
repo-token:
required: false
default: ''

outputs:
cli-output:
Expand All @@ -43,13 +54,16 @@ runs:
env:
SAFETY_API_KEY: ${{ inputs.api-key }}
SAFETY_ACTION: true
SAFETY_ACTION_CREATE_PR: ${{ inputs.create-pr }}
SAFETY_ACTION_CREATE_ISSUE: ${{ inputs.create-issue }}
SAFETY_ACTION_SCAN: ${{ inputs.scan }}
SAFETY_ACTION_DOCKER_IMAGE: ${{ inputs.docker-image }}
SAFETY_ACTION_REQUIREMENTS: ${{ inputs.requirements }}
SAFETY_ACTION_CONTINUE_ON_ERROR: ${{ inputs.continue-on-error }}
SAFETY_ACTION_OUTPUT_FORMAT: ${{ inputs.output-format }}
SAFETY_ACTION_ARGS: ${{ inputs.args }}
SAFETY_ACTION_FORMAT: true
GITHUB_TOKEN: ${{ inputs.repo-token }}
COLUMNS: 120

branding:
Expand Down
37 changes: 37 additions & 0 deletions entrypoint.sh
Expand Up @@ -65,6 +65,43 @@ if [ "${SAFETY_ACTION_SCAN}" = "auto" ]; then
fi
fi

# remediation mode
if [ "${SAFETY_ACTION_CREATE_PR}" = "true" ] && [ "${SAFETY_ACTION_CREATE_ISSUE}" = "true" ]; then
echo "[Safety Action] Can only create issues or PRs, not both."
exit 1
fi

if [ "${SAFETY_ACTION_CREATE_PR}" = "true" ] || [ "${SAFETY_ACTION_CREATE_ISSUE}" = "true" ]; then
if [ "${SAFETY_ACTION_SCAN}" != "file" ]; then
echo "[Safety Action] Creating PRs / issues is only supported when scanning a requirements file."
exit 1
fi

# TODO: Add info to env vars for telemetry...

# Build up a list of requirements files, or use SAFETY_ACTION_REQUIREMENTS if that's set.
# This will be moved into Safety proper in the future.
requirement_files=()
if [ -z "${SAFETY_ACTION_REQUIREMENTS}" ]; then
readarray -d '' matches < <(find . -type f -name requirements.txt -print0)
for match in ${matches[@]}; do
requirement_files+=("-r" "${match}")
done
else
requirement_files=("-r" "${SAFETY_ACTION_REQUIREMENTS}")
fi

alert_action="github-pr"
if [ "${SAFETY_ACTION_CREATE_ISSUE}" = "true" ]; then
alert_action="github-issue"
fi

# Continue on error is set because we're using Safety's output here for further processing.
python -m safety check "${requirement_files[@]}" --continue-on-error --output=json ${SAFETY_ACTION_ARGS} | python -m safety alert "${alert_action}" --repo "${GITHUB_REPOSITORY}" --token "${GITHUB_TOKEN}" --base-url "${GITHUB_API_URL}"

exit 0
fi

if [ "${SAFETY_ACTION_SCAN}" = "docker" ]; then
if [[ "${SAFETY_ACTION_DOCKER_IMAGE}" == "" ]]; then
SAFETY_OS_DESCRIPTION="${SAFETY_OS_DESCRIPTION} docker_image_scan"
Expand Down
42 changes: 42 additions & 0 deletions safety/alerts/__init__.py
@@ -0,0 +1,42 @@
import sys
import json
from typing import Any
import click

from dataclasses import dataclass

from . import github
from safety.util import SafetyPolicyFile

@dataclass
class Alert:
report: Any
key: str
policy: Any = None
requirements_files: Any = None

@click.group(help="Send alerts based on the results of a Safety scan.")
@click.option('--check-report', help='JSON output of Safety Check to work with.', type=click.File('r'), default=sys.stdin)
@click.option("--policy-file", type=SafetyPolicyFile(), default='.safety-policy.yml',
help="Define the policy file to be used")
@click.option("--key", envvar="SAFETY_API_KEY",
help="API Key for pyup.io's vulnerability database. Can be set as SAFETY_API_KEY "
"environment variable.", required=True)
@click.pass_context
def alert(ctx, check_report, policy_file, key):
with check_report:
# TODO: This breaks --help for subcommands
try:
safety_report = json.load(check_report)
except json.decoder.JSONDecodeError as e:
click.secho("Error decoding input JSON: {}".format(e.msg), fg='red')
sys.exit(1)

if not 'report_meta' in safety_report:
click.secho("You must pass in a valid Safety Check JSON report", fg='red')
sys.exit(1)

ctx.obj = Alert(report=safety_report, policy=policy_file if policy_file else {}, key=key)

alert.add_command(github.github_pr)
alert.add_command(github.github_issue)