diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 92aff767..a2617e0f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -35,6 +35,12 @@ stages: docker buildx create --use --name build --node build --driver-opt network=host docker buildx inspect build --bootstrap docker buildx use build + - task: CmdLine@2 + displayName: Release multi arch build + inputs: + script: | + docker buildx build --platform linux/arm64,linux/amd64 -t shiftleft/scan -t shiftleft/sast-scan --push . + condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - task: CmdLine@2 displayName: Multi arch build inputs: @@ -42,12 +48,6 @@ stages: docker buildx build --platform linux/arm64 -t shiftleft/scan:arm -t shiftleft/sast-scan:arm --push . docker buildx build --platform linux/amd64 -t shiftleft/scan:$(tag) -t shiftleft/sast-scan:$(tag) --push . condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master')) - - task: CmdLine@2 - displayName: Release multi arch build - inputs: - script: | - docker buildx build --platform linux/arm64,linux/amd64 -t shiftleft/scan -t shiftleft/sast-scan --push . - condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - task: Docker@2 inputs: @@ -93,6 +93,12 @@ stages: docker buildx create --use --name build --node build --driver-opt network=host docker buildx inspect build --bootstrap docker buildx use build + - task: CmdLine@2 + displayName: Release multi arch build + inputs: + script: | + docker buildx build --platform linux/arm64,linux/amd64 -t shiftleft/scan-slim -f $(Build.SourcesDirectory)/ci/Dockerfile-dynamic-lang --push . + condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - task: CmdLine@2 displayName: Multi arch build inputs: @@ -100,12 +106,6 @@ stages: docker buildx build --platform linux/arm64 -t shiftleft/scan-slim:$(tag) -f $(Build.SourcesDirectory)/ci/Dockerfile-dynamic-lang --push . docker buildx build --platform linux/amd64 -t shiftleft/scan-slim:$(tag) -f $(Build.SourcesDirectory)/ci/Dockerfile-dynamic-lang --push . condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master')) - - task: CmdLine@2 - displayName: Release multi arch build - inputs: - script: | - docker buildx build --platform linux/arm64,linux/amd64 -t shiftleft/scan-slim -f $(Build.SourcesDirectory)/ci/Dockerfile-dynamic-lang --push . - condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - job: Build_oss displayName: Docker Build and Push - oss diff --git a/lib/config.py b/lib/config.py index 9c5ad1cf..056b3c2e 100644 --- a/lib/config.py +++ b/lib/config.py @@ -1127,6 +1127,78 @@ def set(configName, value): "B201": "LOW", "TEMPLATE_INJECTION_FREEMARKER": "MEDIUM", "UNVALIDATED_REDIRECT": "MEDIUM", + "BasicAuth": "MEDIUM", + "BasicAuthTimingAttack": "MEDIUM", + "CSRFTokenForgeryCVE": "HIGH", + "ContentTag": "MEDIUM", + "CookieSerialization": "LOW", + "CreateWith": "LOW", + "CrossSiteScripting": "HIGH", + "DefaultRoutes": "MEDIUM", + "Deserialize": "HIGH", + "DetailedExceptions": "MEDIUM", + "DigestDoS": "HIGH", + "DynamicFinders": "HIGH", + "EscapeFunction": "MEDIUM", + "Evaluation": "HIGH", + "Execute": "HIGH", + "FileAccess": "MEDIUM", + "FileDisclosure": "HIGH", + "FilterSkipping": "MEDIUM", + "ForgerySetting": "MEDIUM", + "HeaderDoS": "HIGH", + "I18nXSS": "MEDIUM", + "JRubyXML": "MEDIUM", + "JSONEncoding": "MEDIUM", + "JSONEntityEscape": "MEDIUM", + "JSONParsing": "HIGH", + "LinkTo": "MEDIUM", + "LinkToHref": "MEDIUM", + "MailTo": "MEDIUM", + "MassAssignment": "MEDIUM", + "MimeTypeDoS": "HIGH", + "ModelAttrAccessible": "LOW", + "ModelAttributes": "MEDIUM", + "ModelSerialize": "MEDIUM", + "NestedAttributes": "LOW", + "NestedAttributesBypass": "LOW", + "NumberToCurrency": "LOW", + "PageCachingCVE": "MEDIUM", + "PermitAttributes": "LOW", + "QuoteTableName": "MEDIUM", + "Redirect": "MEDIUM", + "RegexDoS": "HIGH", + "Render": "MEDIUM", + "RenderDoS": "MEDIUM", + "RenderInline": "LOW", + "ResponseSplitting": "MEDIUM", + "RouteDoS": "HIGH", + "SQL": "HIGH", + "SQLCVEs": "HIGH", + "SSLVerify": "MEDIUM", + "SafeBufferManipulation": "LOW", + "SanitizeMethods": "MEDIUM", + "SelectTag": "MEDIUM", + "SelectVulnerability": "HIGH", + "Send": "MEDIUM", + "SendFile": "MEDIUM", + "SessionManipulation": "HIGH", + "SessionSettings": "MEDIUM", + "SimpleFormat": "LOW", + "SingleQuotes": "LOW", + "SkipBeforeFilter": "MEDIUM", + "SprocketsPathTraversal": "MEDIUM", + "StripTags": "LOW", + "SymbolDoSCVE": "HIGH", + "TemplateInjection": "HIGH", + "TranslateBug": "MEDIUM", + "UnsafeReflection": "LOW", + "UnsafeReflectionMethods": "LOW", + "ValidationRegex": "LOW", + "VerbConfusion": "LOW", + "WithoutProtection": "MEDIUM", + "XMLDoS": "HIGH", + "YAMLParsing": "MEDIUM", } @@ -1140,18 +1212,26 @@ class Cwe(object): SQL_INJECTION = 89 CODE_INJECTION = 94 IMPROPER_WILDCARD_NEUTRALIZATION = 155 + INCORRECT_REGEX = 185 + INFORMATION_DISCLOSURE = 200 HARD_CODED_PASSWORD = 259 IMPROPER_ACCESS_CONTROL = 284 + IMPROPER_AUTHENTICATION = 287 IMPROPER_CERT_VALIDATION = 295 CLEARTEXT_TRANSMISSION = 319 INADEQUATE_ENCRYPTION_STRENGTH = 326 BROKEN_CRYPTO = 327 INSUFFICIENT_RANDOM_VALUES = 330 + CSRF = 352 INSECURE_TEMP_FILE = 377 + SESSION_FIXATION = 384 + IMPROPER_RESOURCE_MANAGEMENT = 399 DESERIALIZATION_OF_UNTRUSTED_DATA = 502 + OPEN_REDIRECT = 601 MULTIPLE_BINDS = 605 IMPROPER_CHECK_OF_EXCEPT_COND = 703 INCORRECT_PERMISSION_ASSIGNMENT = 732 + MASS_ASSIGNMENT = 915 MITRE_URL_PATTERN = "https://cwe.mitre.org/data/definitions/%s.html" @@ -1265,6 +1345,78 @@ def __hash__(self): "B412": Cwe.IMPROPER_ACCESS_CONTROL, "B413": Cwe.BROKEN_CRYPTO, "B414": Cwe.BROKEN_CRYPTO, + "BasicAuth": Cwe.IMPROPER_AUTHENTICATION, + "BasicAuthTimingAttack": Cwe.IMPROPER_AUTHENTICATION, + "CSRFTokenForgeryCVE": Cwe.CSRF, + "ContentTag": Cwe.IMPROPER_INPUT_VALIDATION, + "CookieSerialization": Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA, + "CreateWith": Cwe.MASS_ASSIGNMENT, + "CrossSiteScripting": Cwe.XSS, + "DefaultRoutes": Cwe.PATH_TRAVERSAL, + "Deserialize": Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA, + "DetailedExceptions": Cwe.INFORMATION_DISCLOSURE, + "DigestDoS": Cwe.IMPROPER_AUTHENTICATION, + "DynamicFinders": Cwe.SQL_INJECTION, + "EscapeFunction": Cwe.BASIC_XSS, + "Evaluation": Cwe.CODE_INJECTION, + "Execute": Cwe.OS_COMMAND_INJECTION, + "FileAccess": Cwe.PATH_TRAVERSAL, + "FileDisclosure": Cwe.PATH_TRAVERSAL, + "FilterSkipping": Cwe.IMPROPER_INPUT_VALIDATION, + "ForgerySetting": Cwe.CSRF, + "HeaderDoS": Cwe.IMPROPER_INPUT_VALIDATION, + "I18nXSS": Cwe.BASIC_XSS, + "JRubyXML": Cwe.PATH_TRAVERSAL, + "JSONEncoding": Cwe.BASIC_XSS, + "JSONEntityEscape": Cwe.BASIC_XSS, + "JSONParsing": Cwe.OS_COMMAND_INJECTION, + "LinkTo": Cwe.BASIC_XSS, + "LinkToHref": Cwe.BASIC_XSS, + "MailTo": Cwe.BASIC_XSS, + "MassAssignment": Cwe.MASS_ASSIGNMENT, + "MimeTypeDoS": Cwe.IMPROPER_RESOURCE_MANAGEMENT, + "ModelAttrAccessible": Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA, + "ModelAttributes": Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA, + "ModelSerialize": Cwe.OS_COMMAND_INJECTION, + "NestedAttributes": Cwe.IMPROPER_INPUT_VALIDATION, + "NestedAttributesBypass": Cwe.IMPROPER_ACCESS_CONTROL, + "NumberToCurrency": Cwe.BASIC_XSS, + "PageCachingCVE": Cwe.PATH_TRAVERSAL, + "PermitAttributes": Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA, + "QuoteTableName": Cwe.SQL_INJECTION, + "Redirect": Cwe.OPEN_REDIRECT, + "RegexDoS": Cwe.INCORRECT_REGEX, + "Render": Cwe.PATH_TRAVERSAL, + "RenderDoS": Cwe.IMPROPER_INPUT_VALIDATION, + "RenderInline": Cwe.BASIC_XSS, + "ResponseSplitting": Cwe.CODE_INJECTION, + "RouteDoS": Cwe.IMPROPER_RESOURCE_MANAGEMENT, + "SQL": Cwe.SQL_INJECTION, + "SQLCVEs": Cwe.SQL_INJECTION, + "SSLVerify": Cwe.IMPROPER_CERT_VALIDATION, + "SafeBufferManipulation": Cwe.BASIC_XSS, + "SanitizeMethods": Cwe.BASIC_XSS, + "SelectTag": Cwe.BASIC_XSS, + "SelectVulnerability": Cwe.BASIC_XSS, + "Send": Cwe.IMPROPER_INPUT_VALIDATION, + "SendFile": Cwe.IMPROPER_INPUT_VALIDATION, + "SessionManipulation": Cwe.SESSION_FIXATION, + "SessionSettings": Cwe.SESSION_FIXATION, + "SimpleFormat": Cwe.BASIC_XSS, + "SingleQuotes": Cwe.BASIC_XSS, + "SkipBeforeFilter": Cwe.CSRF, + "SprocketsPathTraversal": Cwe.PATH_TRAVERSAL, + "StripTags": Cwe.BASIC_XSS, + "SymbolDoSCVE": Cwe.IMPROPER_INPUT_VALIDATION, + "TemplateInjection": Cwe.CODE_INJECTION, + "TranslateBug": Cwe.BASIC_XSS, + "UnsafeReflection": Cwe.IMPROPER_INPUT_VALIDATION, + "UnsafeReflectionMethods": Cwe.IMPROPER_INPUT_VALIDATION, + "ValidationRegex": Cwe.IMPROPER_INPUT_VALIDATION, + "VerbConfusion": Cwe.IMPROPER_INPUT_VALIDATION, + "WithoutProtection": Cwe.MASS_ASSIGNMENT, + "XMLDoS": Cwe.OS_COMMAND_INJECTION, + "YAMLParsing": Cwe.OS_COMMAND_INJECTION, } # Mapping of rules and owasp category @@ -1283,6 +1435,7 @@ def __hash__(self): "CWE-94": "a1-injection", "CWE-155": "a1-injection", "CWE-117": "a3-sensitive-data-exposure", + "CWE-185": "a6-misconfiguration", "CWE-203": "a3-sensitive-data-exposure", "CWE-159": "a1-injection", "CWE-259": "a3-sensitive-data-exposure", @@ -1293,12 +1446,14 @@ def __hash__(self): "CWE-327": "a3-sensitive-data-exposure", "CWE-330": "a2-broken-authentication", "CWE-377": "a6-misconfiguration", + "CWE-384": "a5-broken-access-control", "CWE-502": "a8-deserialization", "CWE-601": "a6-misconfiguration", "CWE-605": "a6-misconfiguration", "CWE-644": "a6-misconfiguration", "CWE-703": "a6-misconfiguration", "CWE-732": "a6-misconfiguration", + "CWE-915": "a6-misconfiguration", "CWE-918": "a6-misconfiguration", "rce": "a1-injection", "taint-rce": "a1-injection", diff --git a/lib/pyt/cfg/stmt_visitor.py b/lib/pyt/cfg/stmt_visitor.py index 23f9a119..141bed24 100644 --- a/lib/pyt/cfg/stmt_visitor.py +++ b/lib/pyt/cfg/stmt_visitor.py @@ -215,6 +215,8 @@ def handle_or_else(self, orelse, test): """ if isinstance(orelse[0], ast.If): control_flow_node = self.visit(orelse[0]) + if isinstance(control_flow_node, IgnoredNode): + return IgnoredNode() # Prefix the if label with 'el' control_flow_node.test.label = "el" + control_flow_node.test.label if test is not None: diff --git a/lib/pyt/vulnerability_definitions/all_sources_sinks.pyt b/lib/pyt/vulnerability_definitions/all_sources_sinks.pyt index d7f3675a..77c971e7 100644 --- a/lib/pyt/vulnerability_definitions/all_sources_sinks.pyt +++ b/lib/pyt/vulnerability_definitions/all_sources_sinks.pyt @@ -240,7 +240,8 @@ "tf.io.gfile.rmtree(": {}, "tf.summary.create_file_writer(": {}, "to_disk(": {}, - "document.save(": {} + "document.save(": {}, + "FileSystemLoader(": {} }, "Exfiltration": { "send_file(": {"sanitisers": ["'..'", "'..' in"]}, @@ -474,7 +475,6 @@ "curB.execute(": {"sanitisers": ["bindparams"], "unlisted_args_propagate": false, "arg_dict": {"text": 0}}, "cursor.executemany(": {"sanitisers": ["bindparams"], "unlisted_args_propagate": false, "arg_dict": {"text": 0}}, "cursor.executescript(": {"sanitisers": ["bindparams"], "unlisted_args_propagate": false, "arg_dict": {"text": 0}}, - "execute(": {"sanitisers": ["bindparams"], "unlisted_args_propagate": false, "arg_dict": {"text": 0}}, "executemany(": {"sanitisers": ["bindparams"], "unlisted_args_propagate": false, "arg_dict": {"text": 0}}, "executescript(": {"sanitisers": ["bindparams"], "unlisted_args_propagate": false, "arg_dict": {"text": 0}}, "run_callable(": {"unlisted_args_propagate": false, "arg_dict": {"text": 0}}, diff --git a/lib/pyt/vulnerability_definitions/blackbox_mapping.json b/lib/pyt/vulnerability_definitions/blackbox_mapping.json index a185bd93..ddc791a9 100644 --- a/lib/pyt/vulnerability_definitions/blackbox_mapping.json +++ b/lib/pyt/vulnerability_definitions/blackbox_mapping.json @@ -52,6 +52,13 @@ "environ_property", "UserSerializer", "UserDeserializer", + "URLSafeSerializer", + "URLSafeTimedSerializer", + "TimestampSigner", + "sign", + "validate", + "unsign", + "timestamp_to_datetime", "int", "float", "double", @@ -59,7 +66,16 @@ "AES.new", "aes.encrypt", "Encryption.unpad", - "aes.decrypt" + "aes.decrypt", + "base64_encode", + "base64_decode", + "localize", + "basename", + "getsize", + "create", + "delete", + "filter", + "first" ], "propagates": [ "os.path.join", @@ -91,7 +107,15 @@ "instance.exports", "Instance", "Module.from_file", - "linker.instantiate" + "linker.instantiate", + "loads_unsafe", + "start_response", + "dict", + "copy", + "values", + "file", + "add_message", + "update" ], "safe_decorators": [ "user_must_be_authorized", diff --git a/lib/pyt/web_frameworks/framework_helper.py b/lib/pyt/web_frameworks/framework_helper.py index 6071c4d0..85e259f1 100644 --- a/lib/pyt/web_frameworks/framework_helper.py +++ b/lib/pyt/web_frameworks/framework_helper.py @@ -42,6 +42,7 @@ def is_taintable_function(ast_node): # Flask route and Django tag if _get_last_of_iterable(get_call_names(decorator.func)) in [ "route", + "errorhandler", "simple_tag", "inclusion_tag", "to_end_tag", @@ -67,6 +68,11 @@ def is_taintable_function(ast_node): "before_first_request", "receiver", "require_http_methods", + "application", + "command", + "option", + "group", + "argument", ]: return True # Ignore database functions @@ -75,7 +81,7 @@ def is_taintable_function(ast_node): if first_arg_name == "self" and len(ast_node.args.args) > 1: first_arg_name = ast_node.args.args[1].arg # Common view functions such as django, starlette, falcon - if first_arg_name in ["req", "request", "context", "scope"]: + if first_arg_name in ["req", "request", "context", "scope", "environ"]: return True # Ignore dao classes due to potential FP if first_arg_name in ["conn", "connection", "cls", "session", "session_cls"]: