Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion requirements-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,5 @@ typing_extensions
urllib3
Werkzeug
wrapt
zipp
zipp
nltk
47 changes: 47 additions & 0 deletions src/dylin/analyses/HostnamesTerminatesWithSlash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# ============================== Define spec ==============================
from .base_analysis import BaseDyLinAnalysis
from dynapyt.instrument.filters import only

from typing import Callable, Tuple, Dict


"""
It is recommended to terminate full hostnames with a /.
"""


class HostnamesTerminatesWithSlash(BaseDyLinAnalysis):

def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.analysis_name = "HostnamesTerminatesWithSlash"

@only(patterns=["mount"])
def pre_call(
self, dyn_ast: str, iid: int, function: Callable, pos_args: Tuple, kw_args: Dict
) -> None:
# The target class names for monitoring
targets = ["requests.sessions.Session"]

# Get the class name
if hasattr(function, '__self__') and hasattr(function.__self__, '__class__'):
cls = function.__self__.__class__
class_name = cls.__module__ + "." + cls.__name__
else:
class_name = None

# Check if the class name is the target ones
if class_name in targets:

# Spec content
url = pos_args[0] # Updated to use the first argument as self is not considered here
if not url.endswith('/'):

# Spec content
self.add_finding(
iid,
dyn_ast,
"B-6",
f"The call to method mount in file at {dyn_ast} does not terminate the hostname with a /."
)
# =========================================================================
53 changes: 53 additions & 0 deletions src/dylin/analyses/NLTK_regexp_span_tokenize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# ============================== Define spec ==============================
from .base_analysis import BaseDyLinAnalysis
from dynapyt.instrument.filters import only

from typing import Callable, Tuple, Dict


"""
Regular expression passed to regexp_span_tokenize must not be empty
src: https://www.nltk.org/api/nltk.tokenize.util.html
"""


class NLTK_regexp_span_tokenize(BaseDyLinAnalysis):

def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.analysis_name = "NLTK_regexp_span_tokenize"

@only(patterns=["regexp_span_tokenize"])
def pre_call(
self, dyn_ast: str, iid: int, function: Callable, pos_args: Tuple, kw_args: Dict
) -> None:
# The target class names for monitoring
targets = ["nltk.tokenize.util"]

# Get the class name
if hasattr(function, '__module__'):
class_name = function.__module__
else:
class_name = None

# Check if the class name is the target ones
if class_name in targets:

# Spec content
regexp = None
if kw_args.get('regexp'):
regexp = kw_args['regexp']
elif len(pos_args) > 1:
regexp = pos_args[1]

# Check if the regular expression is empty
if regexp == '':

# Spec content
self.add_finding(
iid,
dyn_ast,
"B-8",
f"Regular expression must not be empty at {dyn_ast}."
)
# =========================================================================
68 changes: 68 additions & 0 deletions src/dylin/analyses/RandomParams_NoPositives.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# ============================== Define spec ==============================
from .base_analysis import BaseDyLinAnalysis
from dynapyt.instrument.filters import only

from typing import Callable, Tuple, Dict


"""
random.lognormvariate(mu, sigma) -> mu can have any value, and sigma must be greater than zero.
random.vonmisesvariate(mu, kappa) -> kappa must be greater than or equal to zero.
"""


class RandomParams_NoPositives(BaseDyLinAnalysis):

def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.analysis_name = "RandomParams_NoPositives"

@only(patterns=["lognormvariate", "vonmisesvariate"])
def pre_call(
self, dyn_ast: str, iid: int, function: Callable, pos_args: Tuple, kw_args: Dict
) -> None:
# The target class names for monitoring
targets = ["random"]

# Get the class name
if hasattr(function, '__module__'):
class_name = function.__module__
else:
class_name = None

# Check if the class name is the target ones
if class_name in targets:

# Check if the parameters are correct
violation = False
if function.__name__ == "lognormvariate":
sigma = None
if kw_args.get('sigma'):
sigma = kw_args['sigma']
elif len(pos_args) > 1:
sigma = pos_args[1]

if sigma is not None and sigma <= 0:
violation = True

else: # Must be vonmisesvariate in this case
kappa = None
if kw_args.get('kappa'):
kappa = kw_args['kappa']
elif len(pos_args) > 1:
kappa = pos_args[1]

if kappa is not None and kappa < 0:
violation = True

# If there is a violation, add a finding
if violation:

# Spec content
self.add_finding(
iid,
dyn_ast,
"B-12",
f"The call to method lognormvariate or vonmisesvariate in file at {dyn_ast} does not have the correct parameters. The sigma or kappa should always be positive."
)
# =========================================================================
45 changes: 45 additions & 0 deletions src/dylin/analyses/RandomRandrange_MustNotUseKwargs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# ============================== Define spec ==============================
from .base_analysis import BaseDyLinAnalysis
from dynapyt.instrument.filters import only

from typing import Callable, Tuple, Dict


"""
Keyword arguments should not be used because they can be interpreted in unexpected ways
source: https://docs.python.org/3/library/random.html#random.randrange
"""


class RandomRandrange_MustNotUseKwargs(BaseDyLinAnalysis):

def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.analysis_name = "RandomRandrange_MustNotUseKwargs"

@only(patterns=["randrange"])
def pre_call(
self, dyn_ast: str, iid: int, function: Callable, pos_args: Tuple, kw_args: Dict
) -> None:
# The target class names for monitoring
targets = ["random"]

# Get the class name
if hasattr(function, '__module__'):
class_name = function.__module__
else:
class_name = None

# Check if the class name is the target ones
if class_name in targets:

# Spec content
if kw_args:
# Spec content
self.add_finding(
iid,
dyn_ast,
"B-13",
f"Keyword arguments should not be used in random.randrange because they can be interpreted in unexpected ways at {dyn_ast}."
)
# =========================================================================
54 changes: 54 additions & 0 deletions src/dylin/analyses/Requests_DataMustOpenInBinary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# ============================== Define spec ==============================
from .base_analysis import BaseDyLinAnalysis
from dynapyt.instrument.filters import only

from typing import Callable, Tuple, Dict


"""
It is strongly recommended that you open files in binary mode. This is because Requests may attempt to provide
the Content-Length header for you, and if it does this value will be set to the number of bytes in the file.
Errors may occur if you open the file in text mode.
"""


class Requests_DataMustOpenInBinary(BaseDyLinAnalysis):

def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.analysis_name = "Requests_DataMustOpenInBinary"

@only(patterns=["post"])
def pre_call(
self, dyn_ast: str, iid: int, function: Callable, pos_args: Tuple, kw_args: Dict
) -> None:
# The target class names for monitoring
targets = ["requests.api"]

# Get the class name
if hasattr(function, '__module__'):
class_name = function.__module__
else:
class_name = None

# Check if the class name is the target ones
if class_name in targets:

# Check if the data is a file
kwords = ['data', 'files']
for k in kwords:
if k in kw_args:
data = kw_args[k]
if hasattr(data, 'read') and hasattr(data, 'mode') and 'b' not in data.mode:

# Spec content
self.add_finding(
iid,
dyn_ast,
"B-14",
f"It is strongly recommended that you open files in binary mode at {dyn_ast}. "
f"This is because Requests may attempt to provide the Content-Length header for you, "
f"and if it does this value will be set to the number of bytes in the file. "
f"Errors may occur if you open the file in text mode."
)
# =========================================================================
55 changes: 55 additions & 0 deletions src/dylin/analyses/Session_DataMustOpenInBinary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# ============================== Define spec ==============================
from .base_analysis import BaseDyLinAnalysis
from dynapyt.instrument.filters import only

from typing import Callable, Tuple, Dict


"""
It is strongly recommended that you open files in binary mode. This is because Requests may attempt to provide
the Content-Length header for you, and if it does this value will be set to the number of bytes in the file.
Errors may occur if you open the file in text mode.
"""


class Session_DataMustOpenInBinary(BaseDyLinAnalysis):

def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.analysis_name = "Session_DataMustOpenInBinary"

@only(patterns=["post"])
def pre_call(
self, dyn_ast: str, iid: int, function: Callable, pos_args: Tuple, kw_args: Dict
) -> None:
# The target class names for monitoring
targets = ["requests.sessions.Session"]

# Get the class name
if hasattr(function, '__self__') and hasattr(function.__self__, '__class__'):
cls = function.__self__.__class__
class_name = cls.__module__ + "." + cls.__name__
else:
class_name = None

# Check if the class name is the target ones
if class_name in targets:

# Spec content
kwords = ['data', 'files']
for k in kwords:
if k in kw_args:
data = kw_args[k]
if hasattr(data, 'read') and hasattr(data, 'mode') and 'b' not in data.mode:

# Spec content
self.add_finding(
iid,
dyn_ast,
"B-15",
f"It is strongly recommended that you open files in binary mode at {dyn_ast}. "
f"This is because Requests may attempt to provide the Content-Length header for you, "
f"and if it does this value will be set to the number of bytes in the file. "
f"Errors may occur if you open the file in text mode."
)
# =========================================================================
Loading