New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

python-taint

Package Overview
Dependencies
Maintainers
3
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

python-taint - pypi Package Compare versions

Comparing version
0.38
to
0.39
+3
-2
PKG-INFO
Metadata-Version: 1.1
Name: python-taint
Version: 0.38
Version: 0.39
Summary: Find security vulnerabilities in Python web applications using static analysis.

@@ -9,3 +9,4 @@ Home-page: https://github.com/python-security/pyt

License: GPLv2
Download-URL: https://github.com/python-security/pyt/archive/0.38.tar.gz
Download-URL: https://github.com/python-security/pyt/archive/0.39.tar.gz
Description-Content-Type: UNKNOWN
Description: Check out PyT on `GitHub <https://github.com/python-security/pyt>`_!

@@ -12,0 +13,0 @@ Keywords: security,vulnerability,web,flask,django,static-analysis,program-analysis

+10
-8

@@ -68,7 +68,5 @@ """The comand line module of PyT."""

ui_mode = UImode.NORMAL
ui_mode = UImode.TRIM
if args.interactive:
ui_mode = UImode.INTERACTIVE
elif args.trim_reassigned_in:
ui_mode = UImode.TRIM

@@ -83,11 +81,15 @@ files = discover_files(

for path in files:
if args.project_root:
directory = os.path.normpath(args.project_root)
project_modules = get_modules(directory, prepend_module_root=args.prepend_module_root)
cfg_list = list()
for path in sorted(files):
if not args.ignore_nosec:
nosec_lines[path] = retrieve_nosec_lines(path)
if args.project_root:
directory = os.path.normpath(args.project_root)
else:
if not args.project_root:
directory = os.path.dirname(path)
project_modules = get_modules(directory, prepend_module_root=args.prepend_module_root)
project_modules = get_modules(directory, prepend_module_root=args.prepend_module_root)
local_modules = get_directory_modules(directory)

@@ -94,0 +96,0 @@ tree = generate_ast(path)

@@ -14,2 +14,3 @@ import ast

generate_ast,
get_call_names,
get_call_names_as_string

@@ -330,7 +331,7 @@ )

def assign_tuple_target(self, node, right_hand_side_variables):
def assign_tuple_target(self, target_nodes, value_nodes, right_hand_side_variables):
new_assignment_nodes = []
remaining_variables = list(right_hand_side_variables)
remaining_targets = list(node.targets[0].elts)
remaining_values = list(node.value.elts) # May contain duplicates
remaining_targets = list(target_nodes)
remaining_values = list(value_nodes) # May contain duplicates

@@ -344,3 +345,3 @@ def visit(target, value):

new_ast_node = ast.Assign(target, value)
ast.copy_location(new_ast_node, node)
ast.copy_location(new_ast_node, target)
new_assignment_nodes.append(self.assignment_call_node(label.result, new_ast_node))

@@ -355,3 +356,3 @@ else:

rhs_visitor.result,
line_number=node.lineno,
line_number=target.lineno,
path=self.filenames[-1]

@@ -365,3 +366,3 @@ )))

# Pair targets and values until a Starred node is reached
for target, value in zip(node.targets[0].elts, node.value.elts):
for target, value in zip(target_nodes, value_nodes):
if isinstance(target, ast.Starred) or isinstance(value, ast.Starred):

@@ -388,3 +389,3 @@ break

remaining_variables,
line_number=node.lineno,
line_number=target.lineno,
path=self.filenames[-1]

@@ -422,3 +423,3 @@ )))

if isinstance(node.value, (ast.Tuple, ast.List)):
return self.assign_tuple_target(node, rhs_visitor.result)
return self.assign_tuple_target(node.targets[0].elts, node.value.elts, rhs_visitor.result)
elif isinstance(node.value, ast.Call):

@@ -431,2 +432,6 @@ call = None

return call
elif isinstance(node.value, ast.Name): # Treat `x, y = z` like `x, y = (*z,)`
value_node = ast.Starred(node.value, ast.Load())
ast.copy_location(value_node, node)
return self.assign_tuple_target(node.targets[0].elts, [value_node], rhs_visitor.result)
else:

@@ -479,10 +484,2 @@ label = LabelVisitor()

if isinstance(call, BBorBInode):
# Necessary to know e.g.
# `image_name = image_name.replace('..', '')`
# is a reassignment.
vars_visitor = VarsVisitor()
vars_visitor.visit(ast_node.value)
call.right_hand_side_variables.extend(vars_visitor.result)
call_assignment = AssignmentCallNode(

@@ -580,3 +577,3 @@ left_hand_label + ' = ' + call_label,

def add_blackbox_or_builtin_call(self, node, blackbox):
def add_blackbox_or_builtin_call(self, node, blackbox): # noqa: C901
"""Processes a blackbox or builtin function when it is called.

@@ -606,10 +603,10 @@ Nothing gets assigned to ret_func_foo in the builtin/blackbox case.

call_label = LabelVisitor()
call_label.visit(node)
call_label_visitor = LabelVisitor()
call_label_visitor.visit(node)
index = call_label.result.find('(')
call_function_label = call_label_visitor.result[:call_label_visitor.result.find('(')]
# Create e.g. ~call_1 = ret_func_foo
LHS = CALL_IDENTIFIER + 'call_' + str(saved_function_call_index)
RHS = 'ret_' + call_label.result[:index] + '('
RHS = 'ret_' + call_function_label + '('

@@ -623,3 +620,3 @@ call_node = BBorBInode(

path=self.filenames[-1],
func_name=call_label.result[:index]
func_name=call_function_label
)

@@ -630,3 +627,4 @@ visual_args = list()

for arg in itertools.chain(node.args, node.keywords):
for arg_node in itertools.chain(node.args, node.keywords):
arg = arg_node.value if isinstance(arg_node, ast.keyword) else arg_node
if isinstance(arg, ast.Call):

@@ -650,11 +648,14 @@ return_value_of_nested_call = self.visit(arg)

visual_args.append(return_value_of_nested_call.left_hand_side)
if isinstance(arg_node, ast.keyword) and arg_node.arg is not None:
visual_args.append(arg_node.arg + '=' + return_value_of_nested_call.left_hand_side)
else:
visual_args.append(return_value_of_nested_call.left_hand_side)
rhs_vars.append(return_value_of_nested_call.left_hand_side)
else:
label = LabelVisitor()
label.visit(arg)
label.visit(arg_node)
visual_args.append(label.result)
vv = VarsVisitor()
vv.visit(arg)
vv.visit(arg_node)
rhs_vars.extend(vv.result)

@@ -666,2 +667,7 @@ if last_return_value_of_nested_call:

call_names = list(get_call_names(node.func))
if len(call_names) > 1:
# taint is a RHS variable (self) of taint.lower()
rhs_vars.append(call_names[0])
if len(visual_args) > 0:

@@ -677,3 +683,2 @@ for arg in visual_args:

call_node.right_hand_side_variables = rhs_vars
# Used in get_sink_args, not using right_hand_side_variables because it is extended in assignment_call_node
rhs_visitor = RHSVisitor()

@@ -680,0 +685,0 @@ rhs_visitor.visit(node)

@@ -87,3 +87,4 @@ import ast

self.visit(node.func)
for arg in itertools.chain(node.args, node.keywords):
for arg_node in itertools.chain(node.args, node.keywords):
arg = arg_node.value if isinstance(arg_node, ast.keyword) else arg_node
if isinstance(arg, ast.Call):

@@ -99,8 +100,28 @@ if isinstance(arg.func, ast.Name):

self.result.append('ret_' + arg.func.attr)
elif isinstance(arg.func, ast.Call):
self.visit_curried_call_inside_call_args(arg)
else:
# Deal with it when we have code that triggers it.
raise
raise Exception('Cannot visit vars of ' + ast.dump(arg))
else:
self.visit(arg)
def visit_curried_call_inside_call_args(self, inner_call):
# Curried functions aren't supported really, but we now at least have a defined behaviour.
# In f(g(a)(b)(c)), inner_call is the Call node with argument c
# Try to get the name of curried function g
curried_func = inner_call.func.func
while isinstance(curried_func, ast.Call):
curried_func = curried_func.func
if isinstance(curried_func, ast.Name):
self.result.append('ret_' + curried_func.id)
elif isinstance(curried_func, ast.Attribute):
self.result.append('ret_' + curried_func.attr)
# Visit all arguments except a (ignore the curried function g)
not_curried = inner_call
while not_curried.func is not curried_func:
for arg in itertools.chain(not_curried.args, not_curried.keywords):
self.visit(arg.value if isinstance(arg, ast.keyword) else arg)
not_curried = not_curried.func
def visit_Attribute(self, node):

@@ -107,0 +128,0 @@ if not isinstance(node.value, ast.Name):

@@ -6,2 +6,3 @@ """This module contains vulnerability types, Enums, nodes and helpers."""

from collections import namedtuple
from itertools import takewhile

@@ -60,12 +61,9 @@ from ..core.node_types import YieldNode

self.reassignment_nodes = reassignment_nodes
self._remove_sink_from_secondary_nodes()
# Remove the sink node and all nodes after the sink from the list of reassignments.
self.reassignment_nodes = list(takewhile(
lambda node: node is not sink,
reassignment_nodes
))
self._remove_non_propagating_yields()
def _remove_sink_from_secondary_nodes(self):
try:
self.reassignment_nodes.remove(self.sink)
except ValueError: # pragma: no cover
pass
def _remove_non_propagating_yields(self):

@@ -72,0 +70,0 @@ """Remove yield with no variables e.g. `yield 123` and plain `yield` from vulnerability."""

Metadata-Version: 1.1
Name: python-taint
Version: 0.38
Version: 0.39
Summary: Find security vulnerabilities in Python web applications using static analysis.

@@ -9,3 +9,4 @@ Home-page: https://github.com/python-security/pyt

License: GPLv2
Download-URL: https://github.com/python-security/pyt/archive/0.38.tar.gz
Download-URL: https://github.com/python-security/pyt/archive/0.39.tar.gz
Description-Content-Type: UNKNOWN
Description: Check out PyT on `GitHub <https://github.com/python-security/pyt>`_!

@@ -12,0 +13,0 @@ Keywords: security,vulnerability,web,flask,django,static-analysis,program-analysis

@@ -54,9 +54,31 @@ .. image:: https://travis-ci.org/python-security/pyt.svg?branch=master

How It Works
How it Works
============
Soon you will find a README.rst in every directory in the pyt folder, `start here`_.
Soon you will find a `README.rst`_ in every directory in the ``pyt/`` folder, `start here`_.
.. _README.rst: https://github.com/python-security/pyt/tree/master/pyt
.. _start here: https://github.com/python-security/pyt/tree/master/pyt
How to Use
============
1. Choose a web framework
`The -a option determines which functions will have their arguments tainted`_, by default it is Flask.
2. (optional) Customize source and sink information
Use the ``-t`` option to specify sources and sinks, by default `this file is used`_.
3. (optional) Customize which library functions propagate taint
For functions from builtins or libraries, e.g. ``url_for`` or ``os.path.join``, use the ``-m`` option to specify whether or not they return tainted values given tainted inputs, by `default this file is used`_.
.. _The -a option determines which functions will have their arguments tainted: https://github.com/python-security/pyt/tree/master/pyt/web_frameworks#web-frameworks
.. _this file is used: https://github.com/python-security/pyt/blob/master/pyt/vulnerability_definitions/all_trigger_words.pyt
.. _default this file is used: https://github.com/python-security/pyt/blob/master/pyt/vulnerability_definitions/blackbox_mapping.json
Usage

@@ -76,24 +98,35 @@ =====

optional arguments:
important optional arguments:
-a ADAPTOR, --adaptor ADAPTOR
Choose a web framework adaptor: Flask(Default),
Django, Every or Pylons
-t TRIGGER_WORD_FILE, --trigger-word-file TRIGGER_WORD_FILE
Input file with a list of sources and sinks
-m BLACKBOX_MAPPING_FILE, --blackbox-mapping-file BLACKBOX_MAPPING_FILE
Input blackbox mapping file
optional arguments:
-pr PROJECT_ROOT, --project-root PROJECT_ROOT
Add project root, only important when the entry file
is not at the root of the project.
is not at the root of the project
-b BASELINE_JSON_FILE, --baseline BASELINE_JSON_FILE
Path of a baseline report to compare against (only
JSON-formatted files are accepted)
-j, --json Prints JSON instead of report.
-m BLACKBOX_MAPPING_FILE, --blackbox-mapping-file BLACKBOX_MAPPING_FILE
Input blackbox mapping file.
-t TRIGGER_WORD_FILE, --trigger-word-file TRIGGER_WORD_FILE
Input file with a list of sources and sinks
-j, --json Prints JSON instead of report
-o OUTPUT_FILE, --output OUTPUT_FILE
write report to filename
--ignore-nosec do not skip lines with # nosec comments
-r, --recursive find and process files in subdirectories
Write report to filename
--ignore-nosec Do not skip lines with # nosec comments
-r, --recursive Find and process files in subdirectories
-x EXCLUDED_PATHS, --exclude EXCLUDED_PATHS
Separate files with commas
print arguments:

@@ -103,3 +136,3 @@ -trim, --trim-reassigned-in

chain.
-i, --interactive Will ask you about each blackbox function call in
-i, --interactive Will ask you about each blackbox function call in
vulnerability chains.

@@ -106,0 +139,0 @@

@@ -5,5 +5,4 @@ [metadata]

[egg_info]
tag_svn_revision = 0
tag_build =
tag_date = 0
tag_build =

@@ -5,3 +5,3 @@ from setuptools import find_packages

VERSION = '0.38'
VERSION = '0.39'

@@ -8,0 +8,0 @@