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

Support scanning setup.py files #108

Open
voidus opened this issue Feb 7, 2018 · 2 comments
Open

Support scanning setup.py files #108

voidus opened this issue Feb 7, 2018 · 2 comments
Labels
considering Indicates that the issue is under consideration. enhancement Suggests an improvement or new feature.

Comments

@voidus
Copy link

voidus commented Feb 7, 2018

It would be nice if it was possible to pass setup.py files to safety check --file

@calve
Copy link

calve commented Mar 21, 2018

The following script might be a starting point, just pipe the output to safety. Be careful with the input tho as this use ast.parse() and could happily execute malicious code in the setup.py.

#!/usr/bin/env python3                                                                                                                                                                                                                                                            
"""                                                                                                                                                                                                                                                                               
Returns a safety-readable list of requirements from a setup.py python script                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                  
Code heavily inspired by https://stackoverflow.com/a/24237067                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                  
Usage:                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                  
    $ ./parse_setup.py /path/to/setup.py                                                                                                                                                                                                                                          
"""
import ast
import sys
import textwrap

def parse_setup(setup_filename):
    """Parse setup.py and return args and keywords args to its setup                                                                                                                                                                                                              
    function call                                                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                  
    """
    mock_setup = textwrap.dedent('''\                                                                                                                                                                                                                                             
    def setup(*args, **kwargs):                                                                                                                                                                                                                                                   
        __setup_calls__.append((args, kwargs))                                                                                                                                                                                                                                    
    ''')
    parsed_mock_setup = ast.parse(mock_setup, filename=setup_filename)
    with open(setup_filename, 'rt') as setup_file:
        parsed = ast.parse(setup_file.read())
        for index, node in enumerate(parsed.body[:]):
            if (
                not isinstance(node, ast.Expr) or
                not isinstance(node.value, ast.Call) or
                node.value.func.id != 'setup'
            ):
                continue
            parsed.body[index:index] = parsed_mock_setup.body
            break

    fixed = ast.fix_missing_locations(parsed)
    codeobj = compile(fixed, setup_filename, 'exec')
    local_vars = {}
    global_vars = {'__setup_calls__': []}
    exec(codeobj, global_vars, local_vars)
    return global_vars['__setup_calls__'][0]

if __name__ == '__main__':
    filename = sys.argv[1]
    _, kwargs = parse_setup(filename)
    requires = kwargs['install_requires']

    print('\n'.join(requires))

@rafaelpivato rafaelpivato added enhancement Suggests an improvement or new feature. considering Indicates that the issue is under consideration. labels Mar 23, 2020
@rafaelpivato
Copy link
Contributor

Apparently #218 might solve your issue here. Well, at least to integrate safety with your setup.py. Now, to literally scan a setup.py file "from outside" as a static analyzer, then we might need dparse support and a better use of dparse by Safety.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
considering Indicates that the issue is under consideration. enhancement Suggests an improvement or new feature.
Projects
None yet
Development

No branches or pull requests

3 participants