forked from VimPlug/jedi
Remove a possible security issue
sys paths are not executed anymore and use static analysis now.
This commit is contained in:
@@ -174,14 +174,6 @@ except NameError:
|
|||||||
unicode = str
|
unicode = str
|
||||||
|
|
||||||
|
|
||||||
# exec function
|
|
||||||
if is_py3:
|
|
||||||
def exec_function(source, global_map):
|
|
||||||
exec(source, global_map)
|
|
||||||
else:
|
|
||||||
eval(compile("""def exec_function(source, global_map):
|
|
||||||
exec source in global_map """, 'blub', 'exec'))
|
|
||||||
|
|
||||||
# re-raise function
|
# re-raise function
|
||||||
if is_py3:
|
if is_py3:
|
||||||
def reraise(exception, traceback):
|
def reraise(exception, traceback):
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import sys
|
|||||||
import imp
|
import imp
|
||||||
from jedi.evaluate.site import addsitedir
|
from jedi.evaluate.site import addsitedir
|
||||||
|
|
||||||
from jedi._compatibility import exec_function, unicode
|
from jedi._compatibility import unicode
|
||||||
from jedi.evaluate.cache import evaluator_function_cache
|
from jedi.evaluate.cache import evaluator_function_cache
|
||||||
from jedi.evaluate.base_context import ContextualizedNode
|
from jedi.evaluate.base_context import ContextualizedNode
|
||||||
|
from jedi.evaluate.helpers import is_string
|
||||||
from jedi import settings
|
from jedi import settings
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.evaluate.utils import ignored
|
from jedi.evaluate.utils import ignored
|
||||||
@@ -68,7 +69,8 @@ def _get_venv_sitepackages(venv):
|
|||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
def _abs_path(module_path, path):
|
def _abs_path(module_context, path):
|
||||||
|
module_path = module_context.py__file__()
|
||||||
if os.path.isabs(path):
|
if os.path.isabs(path):
|
||||||
return path
|
return path
|
||||||
|
|
||||||
@@ -78,6 +80,7 @@ def _abs_path(module_path, path):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
base_dir = os.path.dirname(module_path)
|
base_dir = os.path.dirname(module_path)
|
||||||
|
print(base_dir, path)
|
||||||
return os.path.abspath(os.path.join(base_dir, path))
|
return os.path.abspath(os.path.join(base_dir, path))
|
||||||
|
|
||||||
|
|
||||||
@@ -115,50 +118,36 @@ def _paths_from_assignment(module_context, expr_stmt):
|
|||||||
except AssertionError:
|
except AssertionError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
from jedi.evaluate.syntax_tree import is_string
|
|
||||||
cn = ContextualizedNode(module_context.create_context(expr_stmt), expr_stmt)
|
cn = ContextualizedNode(module_context.create_context(expr_stmt), expr_stmt)
|
||||||
for lazy_context in cn.infer().iterate(cn):
|
for lazy_context in cn.infer().iterate(cn):
|
||||||
for context in lazy_context.infer():
|
for context in lazy_context.infer():
|
||||||
if is_string(context):
|
if is_string(context):
|
||||||
abs_path = _abs_path(module_context.py__file__(), context.obj)
|
abs_path = _abs_path(module_context, context.obj)
|
||||||
if abs_path is not None:
|
if abs_path is not None:
|
||||||
yield abs_path
|
yield abs_path
|
||||||
|
|
||||||
|
|
||||||
def _execute_code(module_path, code):
|
def _paths_from_list_modifications(module_context, trailer1, trailer2):
|
||||||
c = "import os; from os.path import *; result=%s"
|
|
||||||
variables = {'__file__': module_path}
|
|
||||||
try:
|
|
||||||
exec_function(c % code, variables)
|
|
||||||
except Exception:
|
|
||||||
debug.warning('sys.path manipulation detected, but failed to evaluate.')
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
res = variables['result']
|
|
||||||
if isinstance(res, str):
|
|
||||||
return _abs_path(module_path, res)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _paths_from_list_modifications(module_path, trailer1, trailer2):
|
|
||||||
""" extract the path from either "sys.path.append" or "sys.path.insert" """
|
""" extract the path from either "sys.path.append" or "sys.path.insert" """
|
||||||
# Guarantee that both are trailers, the first one a name and the second one
|
# Guarantee that both are trailers, the first one a name and the second one
|
||||||
# a function execution with at least one param.
|
# a function execution with at least one param.
|
||||||
if not (trailer1.type == 'trailer' and trailer1.children[0] == '.'
|
if not (trailer1.type == 'trailer' and trailer1.children[0] == '.'
|
||||||
and trailer2.type == 'trailer' and trailer2.children[0] == '('
|
and trailer2.type == 'trailer' and trailer2.children[0] == '('
|
||||||
and len(trailer2.children) == 3):
|
and len(trailer2.children) == 3):
|
||||||
return []
|
return
|
||||||
|
|
||||||
name = trailer1.children[1].value
|
name = trailer1.children[1].value
|
||||||
if name not in ['insert', 'append']:
|
if name not in ['insert', 'append']:
|
||||||
return []
|
return
|
||||||
arg = trailer2.children[1]
|
arg = trailer2.children[1]
|
||||||
if name == 'insert' and len(arg.children) in (3, 4): # Possible trailing comma.
|
if name == 'insert' and len(arg.children) in (3, 4): # Possible trailing comma.
|
||||||
arg = arg.children[2]
|
arg = arg.children[2]
|
||||||
path = _execute_code(module_path, arg.get_code())
|
|
||||||
return [] if path is None else [path]
|
for context in module_context.create_context(arg).eval_node(arg):
|
||||||
|
if is_string(context):
|
||||||
|
abs_path = _abs_path(module_context, context.obj)
|
||||||
|
if abs_path is not None:
|
||||||
|
yield abs_path
|
||||||
|
|
||||||
|
|
||||||
def check_sys_path_modifications(module_context):
|
def check_sys_path_modifications(module_context):
|
||||||
@@ -190,7 +179,7 @@ def check_sys_path_modifications(module_context):
|
|||||||
if len(power.children) >= 4:
|
if len(power.children) >= 4:
|
||||||
added.extend(
|
added.extend(
|
||||||
_paths_from_list_modifications(
|
_paths_from_list_modifications(
|
||||||
module_context.py__file__(), *power.children[2:4]
|
module_context, *power.children[2:4]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif expr_stmt is not None and expr_stmt.type == 'expr_stmt':
|
elif expr_stmt is not None and expr_stmt.type == 'expr_stmt':
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ sys.path.insert(0, '../../jedi')
|
|||||||
sys.path.append(dirname(os.path.abspath('thirdparty' + os.path.sep + 'asdf')))
|
sys.path.append(dirname(os.path.abspath('thirdparty' + os.path.sep + 'asdf')))
|
||||||
|
|
||||||
# modifications, that should fail:
|
# modifications, that should fail:
|
||||||
# because of sys module
|
|
||||||
sys.path.append(sys.path[1] + '/thirdparty')
|
|
||||||
# syntax err
|
# syntax err
|
||||||
sys.path.append('a' +* '/thirdparty')
|
sys.path.append('a' +* '/thirdparty')
|
||||||
|
|
||||||
@@ -18,8 +16,9 @@ import evaluate
|
|||||||
#? ['evaluator_function_cache']
|
#? ['evaluator_function_cache']
|
||||||
evaluate.Evaluator_fu
|
evaluate.Evaluator_fu
|
||||||
|
|
||||||
#? ['jedi_']
|
# Those don't work because dirname and abspath are not properly understood.
|
||||||
|
##? ['jedi_']
|
||||||
import jedi_
|
import jedi_
|
||||||
|
|
||||||
#? ['el']
|
##? ['el']
|
||||||
jedi_.el
|
jedi_.el
|
||||||
|
|||||||
@@ -4,10 +4,18 @@ Tests of ``jedi.api.Interpreter``.
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import jedi
|
import jedi
|
||||||
from jedi._compatibility import is_py33, exec_function, py_version
|
from jedi._compatibility import is_py33, is_py3
|
||||||
from jedi.evaluate.compiled import mixed
|
from jedi.evaluate.compiled import mixed
|
||||||
|
|
||||||
|
|
||||||
|
if is_py3:
|
||||||
|
def exec_(source, global_map):
|
||||||
|
exec(source, global_map)
|
||||||
|
else:
|
||||||
|
eval(compile("""def exec_(source, global_map):
|
||||||
|
exec source in global_map """, 'blub', 'exec'))
|
||||||
|
|
||||||
|
|
||||||
class _GlobalNameSpace():
|
class _GlobalNameSpace():
|
||||||
class SideEffectContainer():
|
class SideEffectContainer():
|
||||||
pass
|
pass
|
||||||
@@ -247,7 +255,7 @@ def test_completion_param_annotations():
|
|||||||
# Need to define this function not directly in Python. Otherwise Jedi is to
|
# Need to define this function not directly in Python. Otherwise Jedi is to
|
||||||
# clever and uses the Python code instead of the signature object.
|
# clever and uses the Python code instead of the signature object.
|
||||||
code = 'def foo(a: 1, b: str, c: int = 1.0): pass'
|
code = 'def foo(a: 1, b: str, c: int = 1.0): pass'
|
||||||
exec_function(code, locals())
|
exec_(code, locals())
|
||||||
script = jedi.Interpreter('foo', [locals()])
|
script = jedi.Interpreter('foo', [locals()])
|
||||||
c, = script.completions()
|
c, = script.completions()
|
||||||
a, b, c = c.params
|
a, b, c = c.params
|
||||||
|
|||||||
Reference in New Issue
Block a user