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

Multi threat libs #263

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,4 @@ plantuml.jar
tm/
/sqldump
/tests/.config.pytm
.aider*
49 changes: 35 additions & 14 deletions pytm/pytm.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def __init__(self, e, context):


logger = logging.getLogger(__name__)
_defaultThreatsFile = os.path.dirname(__file__) + "/threatlib/threats.json"


class var(object):
Expand Down Expand Up @@ -787,11 +788,7 @@ class TM:
)
name = varString("", required=True, doc="Model name")
description = varString("", required=True, doc="Model description")
threatsFile = varString(
os.path.dirname(__file__) + "/threatlib/threats.json",
onSet=lambda i, v: i._init_threats(),
doc="JSON file with custom threats",
)
threatsFile = varStrings([_defaultThreatsFile])
isOrdered = varBool(False, doc="Automatically order all Dataflows")
mergeResponses = varBool(False, doc="Merge response edges in DFDs")
ignoreUnused = varBool(False, doc="Ignore elements not used in any Dataflow")
Expand Down Expand Up @@ -838,16 +835,19 @@ def _init_threats(self):
self._add_threats()

def _add_threats(self):
try:
with open(self.threatsFile, "r", encoding="utf8") as threat_file:
threats_json = json.load(threat_file)
except (FileNotFoundError, PermissionError, IsADirectoryError) as e:
raise UIError(
e, f"while trying to open the the threat file ({self.threatsFile})."

for tf in self.threatsFile:
try:
with open(tf, "r", encoding="utf8") as threat_file:
threats_json = json.load(threat_file)
except (FileNotFoundError, PermissionError, IsADirectoryError) as e:
raise UIError(
e, f"while trying to open the the threat file ({self.threatsFile})."
)
active_threats = (threat for threat in threats_json if "DEPRECATED" not in threat)
for threat in active_threats:
TM._threats.append(Threat(**threat))

active_threats = (threat for threat in threats_json if "DEPRECATED" not in threat)
for threat in active_threats:
TM._threats.append(Threat(**threat))

def resolve(self):
finding_count = 0
Expand Down Expand Up @@ -1129,6 +1129,21 @@ def _process(self):
result = get_args()
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")

# delaying loading of threats to accomodate multiple threat files in the
# command line
if result.threat_files:
# start by removing the default
del self.threatsFile[0]
if "default" in result.threat_files:
index = result.threat_files.index("default")
result.threat_files[index] = _defaultThreatsFile
for x in result.threat_files:
self.threatsFile.append(x)
else:
# it is just the default file, so no need to do anything
pass
self._init_threats()

if result.debug:
logger.setLevel(logging.DEBUG)

Expand Down Expand Up @@ -1178,6 +1193,7 @@ def _process(self):
if result.stale_days is not None:
print(self._stale(result.stale_days))


def _stale(self, days):
try:
base_path = os.path.dirname(sys.argv[0])
Expand Down Expand Up @@ -2158,6 +2174,11 @@ def get_args():
help="""checks if the delta between the TM script and the code described by it is bigger than the specified value in days""",
type=int,
)
_parser.add_argument(
"--threat-files",
nargs="+",
help="Files containing libraries of threats."
)

_args = _parser.parse_args()
return _args
Binary file removed sample.png
Binary file not shown.
19 changes: 19 additions & 0 deletions tests/1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[
{
"SID": "FOO",
"target": [
"Lambda",
"Process"
],
"description": "FOOOOOOOOOOOOOO",
"details": "This attack pattern involves causing a buffer overflow through manipulation of environment variables. Once the attacker finds that they can modify an environment variable, they may try to overflow associated buffers. This attack leverages implicit trust often placed in environment variables.",
"Likelihood Of Attack": "High",
"severity": "High",
"condition": "target.usesEnvironmentVariables is True and target.controls.sanitizesInput is False and target.controls.checksInputBounds is False",
"prerequisites": "The application uses environment variables.An environment variable exposed to the user is vulnerable to a buffer overflow.The vulnerable environment variable uses untrusted data.Tainted data used in the environment variables is not properly validated. For instance boundary checking is not done before copying the input data to a buffer.",
"mitigations": "Do not expose environment variable to the user.Do not use untrusted data in your environment variables. Use a language or compiler that performs automatic bounds checking. There are tools such as Sharefuzz [R.10.3] which is an environment variable fuzzer for Unix that support loading a shared library. You can use Sharefuzz to determine if you are exposing an environment variable vulnerable to buffer overflow.",
"example": "Attack Example: Buffer Overflow in $HOME A buffer overflow in sccw allows local users to gain root access via the $HOME environmental variable. Attack Example: Buffer Overflow in TERM A buffer overflow in the rlogin program involves its consumption of the TERM environmental variable.",
"references": "https://capec.mitre.org/data/definitions/10.html, CVE-1999-0906, CVE-1999-0046, http://cwe.mitre.org/data/definitions/120.html, http://cwe.mitre.org/data/definitions/119.html, http://cwe.mitre.org/data/definitions/680.html"
}
]

19 changes: 19 additions & 0 deletions tests/2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[
{
"SID": "BAR",
"target": [
"Lambda",
"Process"
],
"description": "FOOOOOOOOOOOOOO",
"details": "This attack pattern involves causing a buffer overflow through manipulation of environment variables. Once the attacker finds that they can modify an environment variable, they may try to overflow associated buffers. This attack leverages implicit trust often placed in environment variables.",
"Likelihood Of Attack": "High",
"severity": "High",
"condition": "target.usesEnvironmentVariables is True and target.controls.sanitizesInput is False and target.controls.checksInputBounds is False",
"prerequisites": "The application uses environment variables.An environment variable exposed to the user is vulnerable to a buffer overflow.The vulnerable environment variable uses untrusted data.Tainted data used in the environment variables is not properly validated. For instance boundary checking is not done before copying the input data to a buffer.",
"mitigations": "Do not expose environment variable to the user.Do not use untrusted data in your environment variables. Use a language or compiler that performs automatic bounds checking. There are tools such as Sharefuzz [R.10.3] which is an environment variable fuzzer for Unix that support loading a shared library. You can use Sharefuzz to determine if you are exposing an environment variable vulnerable to buffer overflow.",
"example": "Attack Example: Buffer Overflow in $HOME A buffer overflow in sccw allows local users to gain root access via the $HOME environmental variable. Attack Example: Buffer Overflow in TERM A buffer overflow in the rlogin program involves its consumption of the TERM environmental variable.",
"references": "https://capec.mitre.org/data/definitions/10.html, CVE-1999-0906, CVE-1999-0046, http://cwe.mitre.org/data/definitions/120.html, http://cwe.mitre.org/data/definitions/119.html, http://cwe.mitre.org/data/definitions/680.html"
}
]

2 changes: 1 addition & 1 deletion tests/output.json
Original file line number Diff line number Diff line change
Expand Up @@ -1405,5 +1405,5 @@
"name": "my test tm",
"onDuplicates": "Action.NO_ACTION",
"threatsExcluded": [],
"threatsFile": "pytm/threatlib/threats.json"
"threatsFile": "{'pytm/threatlib/threats.json'}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the output a string and not a list of strings?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's what varStrings with only one value put out...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a bug?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the immortal words of just about everybody..."works for me"!

}
4 changes: 4 additions & 0 deletions tests/test_private_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,15 @@ def test_kwargs(self):
def test_load_threats(self):
tm = TM("TM")
self.assertNotEqual(len(TM._threats), 0)

''' commenting this bit off since it is now valid to change threatsFile,
but looking for confirmation from Jan that it is ok to do so
with self.assertRaises(UIError):
tm.threatsFile = "threats.json"

with self.assertRaises(UIError):
TM("TM", threatsFile="threats.json")
'''

def test_responses(self):
tm = TM("my test tm", description="aa", isOrdered=True)
Expand Down
17 changes: 17 additions & 0 deletions tests/test_pytmfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,23 @@ def test_json_loads(self):
[f.name for f in tm._flows], ["Request", "Insert", "Select", "Response"]
)

def test_threat_files(self):
dir_path = os.path.dirname(os.path.realpath(__file__))
foo_file = f"{dir_path}/1.json"
bar_file = f"{dir_path}/2.json"
threat_files = [os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
+ "/pytm/threatlib/threats.json", foo_file, bar_file]

TM.reset()
tm = TM("testing multiple threat library files",
description="aaa",
threatsFile=threat_files)
ctr = 0
for t in TM._threats:
if t.id == "FOO" or t.id == "BAR":
ctr += 1
self.assertTrue(ctr == 2)

def test_report(self):
random.seed(0)
dir_path = os.path.dirname(os.path.realpath(__file__))
Expand Down