Skip to content

Commit

Permalink
Add security-severity property to sarif rules (#323)
Browse files Browse the repository at this point in the history
In order for GitHub to render a High, Medium, Low for results it
requires the security-severity to be set.

This also means each rule needs a default level specific to it to be
set. Before it was defaulting to warning, but now will default to the
minimum possible value of level in results.

Signed-off-by: Eric Brown <[email protected]>
  • Loading branch information
ericwb authored Mar 5, 2024
1 parent cc4e560 commit 175c31c
Show file tree
Hide file tree
Showing 28 changed files with 63 additions and 34 deletions.
20 changes: 20 additions & 0 deletions precli/core/level.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,23 @@ class Level(str, enum.Enum):
WARNING = "warning"
NOTE = "note"
NONE = "none"

def to_severity(self) -> float:
"""
Returns a security severity value.
Code scanning translates numerical scores as follows:
over 9.0 is critical, 7.0 to 8.9 is high, 4.0 to 6.9 is medium and
3.9 or less is low.
:return: severity as float
:rtype: float
"""
if self.value == self.ERROR:
return 8.0
elif self.value == self.WARNING:
return 5.0
elif self.value == self.NOTE:
return 3.0
else:
return 0.0
7 changes: 5 additions & 2 deletions precli/renderers/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@


SCHEMA_URI = "https://json.schemastore.org/sarif-2.1.0.json"
SCHEMA_VER = "2.1.0"
TS_FORMAT = "%Y-%m-%dT%H:%M:%SZ"


Expand Down Expand Up @@ -72,6 +73,9 @@ def create_rule_array(self, run: Run):
"security",
f"external/cwe/cwe-{rule.cwe.cwe_id}",
],
"security-severity": (
rule.default_config.level.to_severity()
),
},
)
rules.append(reporting_descriptor)
Expand All @@ -91,14 +95,13 @@ def create_tool_component(self, run: Run):
short_description=sarif_om.MultiformatMessageString(
text=run.tool.short_description
),
version=run.tool.version,
rules=self.create_rule_array(run),
)

def render(self, run: Run):
log = sarif_om.SarifLog(
schema_uri=SCHEMA_URI,
version="2.1.0",
version=SCHEMA_URI,
runs=[
sarif_om.Run(
tool=sarif_om.Tool(driver=self.create_tool_component(run)),
Expand Down
3 changes: 2 additions & 1 deletion precli/rules/go/stdlib/crypto_weak_cipher.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
.. versionadded:: 0.2.1
""" # noqa: E501
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand All @@ -143,6 +144,7 @@ def __init__(self, id: str):
"known vulnerabilities and weaknesses.",
targets=("call"),
wildcards={},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand All @@ -163,7 +165,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.function_node),
level=Level.ERROR,
message=self.message.format(call.name),
fixes=fixes,
)
4 changes: 2 additions & 2 deletions precli/rules/go/stdlib/crypto_weak_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
.. versionadded:: 0.2.1
""" # noqa: E501
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand All @@ -93,6 +94,7 @@ def __init__(self, id: str):
"expectations.",
targets=("call"),
wildcards={},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand All @@ -111,7 +113,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.function_node),
level=Level.ERROR,
message=self.message.format(call.name_qualified),
fixes=fixes,
)
Expand All @@ -128,7 +129,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.function_node),
level=Level.ERROR,
message=self.message.format(call.name_qualified),
fixes=fixes,
)
5 changes: 2 additions & 3 deletions precli/rules/python/stdlib/hashlib_weak_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
""" # noqa: E501
from precli.core.argument import Argument
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand Down Expand Up @@ -117,6 +118,7 @@ def __init__(self, id: str):
"sha1",
]
},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand Down Expand Up @@ -144,7 +146,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.function_node),
level=Level.ERROR,
message=self.message.format(call.name_qualified),
)
elif call.name_qualified in ["hashlib.pbkdf2_hmac"]:
Expand All @@ -163,7 +164,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.function_node),
level=Level.ERROR,
message=self.message.format(hash_name),
)
elif call.name_qualified in ["hashlib.new"]:
Expand All @@ -181,6 +181,5 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.function_node),
level=Level.ERROR,
message=self.message.format(name),
)
3 changes: 2 additions & 1 deletion precli/rules/python/stdlib/hmac_timing_attack.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
.. versionadded:: 0.1.4
""" # noqa: E501
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand Down Expand Up @@ -116,6 +117,7 @@ def __init__(self, id: str):
"HMAC.hexdigest",
]
},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand All @@ -139,7 +141,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=comparison.operator_node),
level=Level.ERROR,
message=self.message.format(comparison.operator),
fixes=fixes,
)
4 changes: 2 additions & 2 deletions precli/rules/python/stdlib/hmac_weak_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
.. versionadded:: 0.1.0
""" # noqa: E501
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand Down Expand Up @@ -110,6 +111,7 @@ def __init__(self, id: str):
"digest",
]
},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand All @@ -128,7 +130,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=argument.node),
level=Level.ERROR,
message=self.message.format(digestmod),
)
elif call.name_qualified in ["hmac.digest"]:
Expand All @@ -144,6 +145,5 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=argument.node),
level=Level.ERROR,
message=self.message.format(digest),
)
3 changes: 2 additions & 1 deletion precli/rules/python/stdlib/http_url_secret.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
from urllib.parse import parse_qs
from urllib.parse import urlsplit

from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand All @@ -88,6 +89,7 @@ def __init__(self, id: str):
"HTTPSConnection",
]
},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand All @@ -109,5 +111,4 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=argument.node),
level=Level.ERROR,
)
3 changes: 2 additions & 1 deletion precli/rules/python/stdlib/imaplib_cleartext.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
.. versionadded:: 0.1.9
""" # noqa: E501
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand All @@ -94,6 +95,7 @@ def __init__(self, id: str):
"IMAP4",
]
},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand Down Expand Up @@ -121,7 +123,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.identifier_node),
level=Level.ERROR,
message=f"The '{call.name_qualified}' function will "
f"transmit authentication information such as a user, "
"password in cleartext.",
Expand Down
3 changes: 2 additions & 1 deletion precli/rules/python/stdlib/nntplib_cleartext.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
.. versionadded:: 0.1.9
""" # noqa: E501
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand All @@ -78,6 +79,7 @@ def __init__(self, id: str):
"NNTP",
]
},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand All @@ -101,7 +103,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.identifier_node),
level=Level.ERROR,
message=f"The '{call.name_qualified}' function will "
f"transmit authentication information such as a user, "
"password in cleartext.",
Expand Down
3 changes: 2 additions & 1 deletion precli/rules/python/stdlib/poplib_cleartext.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
.. versionadded:: 0.1.9
""" # noqa: E501
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand All @@ -90,6 +91,7 @@ def __init__(self, id: str):
"POP3",
]
},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand Down Expand Up @@ -118,7 +120,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.identifier_node),
level=Level.ERROR,
message=f"The '{call.name_qualified}' function will "
f"transmit authentication information such as a user, "
"password in cleartext.",
Expand Down
3 changes: 2 additions & 1 deletion precli/rules/python/stdlib/smtplib_cleartext.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def prompt(prompt):
.. versionadded:: 0.1.9
""" # noqa: E501
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand All @@ -123,6 +124,7 @@ def __init__(self, id: str):
"SMTP",
]
},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand All @@ -149,7 +151,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.identifier_node),
level=Level.ERROR,
message=f"The '{call.name_qualified}' function will "
f"transmit authentication information such as a user, "
"password in cleartext.",
Expand Down
5 changes: 2 additions & 3 deletions precli/rules/python/stdlib/ssl_insecure_tls_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
""" # noqa: E501
from precli.core.argument import Argument
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand All @@ -100,6 +101,7 @@ def __init__(self, id: str):
cwe_id=326,
message="The '{0}' protocol has insufficient encryption strength.",
targets=("call"),
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand Down Expand Up @@ -129,7 +131,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=argument.identifier_node),
level=Level.ERROR,
message=self.message.format(version),
fixes=fixes,
)
Expand Down Expand Up @@ -169,7 +170,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=argument.identifier_node),
level=Level.ERROR,
message=self.message.format(version),
fixes=fixes,
)
Expand All @@ -196,7 +196,6 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=argument.identifier_node),
level=Level.ERROR,
message=self.message.format(protocol),
fixes=fixes,
)
3 changes: 2 additions & 1 deletion precli/rules/python/stdlib/telnetlib_cleartext.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
.. versionadded:: 0.1.0
""" # noqa: E501
from precli.core.config import Config
from precli.core.level import Level
from precli.core.location import Location
from precli.core.result import Result
Expand All @@ -134,6 +135,7 @@ def __init__(self, id: str):
"Telnet",
]
},
config=Config(level=Level.ERROR),
)

def analyze(self, context: dict, **kwargs: dict) -> Result:
Expand All @@ -143,6 +145,5 @@ def analyze(self, context: dict, **kwargs: dict) -> Result:
return Result(
rule_id=self.id,
location=Location(node=call.function_node),
level=Level.ERROR,
message=self.message.format(call.name_qualified),
)
2 changes: 1 addition & 1 deletion tests/unit/rules/go/stdlib/test_crypto_weak_cipher.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_rule_meta(self):
f"https://docs.securesauce.dev/rules/{self.rule_id}", rule.help_url
)
self.assertEqual(True, rule.default_config.enabled)
self.assertEqual(Level.WARNING, rule.default_config.level)
self.assertEqual(Level.ERROR, rule.default_config.level)
self.assertEqual(-1.0, rule.default_config.rank)
self.assertEqual("327", rule.cwe.cwe_id)

Expand Down
Loading

0 comments on commit 175c31c

Please sign in to comment.