pyupio / safety

Safety checks Python dependencies for known security vulnerabilities and suggests the proper remediations for vulnerabilities detected.
https://safetycli.com/product/safety-cli
MIT License
1.7k stars 143 forks source link

Feature request: run from Python #356

Closed joosbuijsNL closed 2 years ago

joosbuijsNL commented 3 years ago

Description

We would like to implement a check at the beginning of our code whether all used packages are safe, according to 'safety'. Is it / would it be possible to run safety from Python, inspecting the current work environment, and allowing the Python code then to determine whether it can continue safely or whether there is an issue? This would put many (manager)minds at ease :)

I'm aware of the possibility of Python to run commands, but I would prefer a more pythonic approach.

What I Did

Read the documentation and search the internet.

turribeach commented 2 years ago

We are interested in this too.

yeisonvargasf commented 2 years ago

Hi! I'm sorry for the late on this thread.

Right now, there is a Safety version 2.0b5 on PyPi.

This version includes support for running Python directly in your code; here there is a small example:

from safety import safety
from safety.formatter import SafetyFormatter

requirements_to_analyze = ['requirements.txt']

files = [open(f, 'r') for f in requirements_to_analyze]

packages = safety.get_packages(files=files, stdin=False)

key = 'YOUR_API_KEY_HERE'
ignore_severity_rules = None
ignore = {}
db = ''
cache = 0
proxy_config = None

vulns, db_full = safety.check(packages=packages, key=key, db_mirror=db, cached=cache, ignore_vulns=ignore, ignore_severity_rules=ignore_severity_rules, proxy=proxy_config,
include_ignored=True)

remediations = safety.calculate_remediations(vulns, db_full)

for vuln in vulns:
    print(vuln.vulnerability_id)
    print(vuln.advisory)

output_report = SafetyFormatter(output='json').render_vulnerabilities([], vulns, remediations, True, packages)

I will close this feature request; if you find issues, file a new issue in this repository. Thanks!

turribeach commented 2 years ago

Thanks, I will give this a go. Quick question, what site do the rules get downloaded from? I will need to open up our internel internet proxy for that...

turribeach commented 2 years ago

Also is there any way to get the requirements.txt' from the current virtual environment safety is running on without passing the requirements.txt to it?

joosbuijsNL commented 2 years ago

We have developed the following function which does not check requirements.txt but all available/installed packages in the current environment, and even quits the application when issues are found.

Use at your own risk.

Any code improvements/suggestions are welcome.

def safety_check(abort=True):
    """
    Performs a safety check on all packages in this Python environment.
    Input parameter:
    - abort (default:True) When True, the application will abort (sys.exit(1) 
    when unsafe packages are found.

    Returns (if abort=False) a list of vulnerabilities or None if all is OK. 
    This allows for a test like `if safety_check(abort=False): DO SOMETHING TO SHUT DOWN`
    """
    # Joos Buijs 2021-09-14
    # Based on https://github.com/pyupio/safety/blob/master/safety/cli.py

    from safety import safety
    from safety.formatter import report
    from safety.util import get_proxy_dict

    import sys

    import pkg_resources
    packages = [
        d for d in pkg_resources.working_set
        if d.key not in {"python", "wsgiref", "argparse"}
    ]    

    abort_text = "We will abort if we find any unsafe packages." if abort else ""
    logger.info(f"Checking {len(packages)} packages for potential security issues before we start. {abort_text}")

    # Command line arguments / defaults
    key = ""
    db = ""
    json = False
    full_report = False
    bare = False
    stdin = False
    files = None
    cache = False
    ignore = []
    output = ""
    proxyprotocol = 'https' # default http
    proxyhost = None
    proxyport = 80

    proxy_dictionary = get_proxy_dict(proxyprotocol, proxyhost, proxyport)
    vulns = safety.check(packages=packages, key=key, db_mirror=db, cached=cache, ignore_ids=ignore, proxy=proxy_dictionary)

    if vulns:
        output_report = report(vulns=vulns, 
                            full=full_report, 
                            json_report=json, 
                            bare_report=bare,
                            checked_packages=len(packages),
                            db=db, 
                            key=key
                            )
        print(output_report) # print/show report for user to act on
        print("The environment is not safe! See the report above for more details.")
        # ABORT your application now!
        if abort:
            sys.exit(1)
        else:
            return output_report
    # Else: all OK, carry on
    return None