diff --git a/checkov/openapi/checks/resource/generic/SecurityOperations.py b/checkov/openapi/checks/resource/generic/SecurityOperations.py index 0b92ed07e4b..dfa17a1d8d3 100644 --- a/checkov/openapi/checks/resource/generic/SecurityOperations.py +++ b/checkov/openapi/checks/resource/generic/SecurityOperations.py @@ -16,8 +16,12 @@ def __init__(self) -> None: block_type=BlockType.DOCUMENT) def scan_entity_conf(self, conf: dict[str, Any], entity_type: str) -> tuple[CheckResult, dict[str, Any]]: # type:ignore[override] # return type is different than the base class - self.evaluated_keys = ['paths'] + self.evaluated_keys = ['security', 'paths'] + # Check if security field is present and not empty at the root level + root_security = conf.get('security') + + # If security field is not present or empty at the root level, check within each operation paths = conf.get('paths', {}) or {} if isinstance(paths, dict): for path, http_method in paths.items(): @@ -30,12 +34,14 @@ def scan_entity_conf(self, conf: dict[str, Any], entity_type: str) -> tuple[Chec self.evaluated_keys = ['security'] if not isinstance(op_val, dict): continue - if 'security' not in op_val: + op_security = op_val.get("security") + if op_security is not None and not op_security: + # fails when security field is set as empty list return CheckResult.FAILED, conf - security = op_val['security'] - if not security: - return CheckResult.FAILED, paths + if op_security is None and not root_security: + # no security field for the operation and not in the root + return CheckResult.FAILED, conf return CheckResult.PASSED, conf diff --git a/tests/openapi/checks/resource/generic/example_SecurityOperations/pass2.json b/tests/openapi/checks/resource/generic/example_SecurityOperations/pass2.json index adb94d6be15..4e55d60d5d2 100644 --- a/tests/openapi/checks/resource/generic/example_SecurityOperations/pass2.json +++ b/tests/openapi/checks/resource/generic/example_SecurityOperations/pass2.json @@ -1,5 +1,5 @@ { - "swagger": "2.0", + "openapi": "3.0.0", "info": { "title": "example", "version": "1.0.0", diff --git a/tests/openapi/checks/resource/generic/example_SecurityOperations/pass2.yaml b/tests/openapi/checks/resource/generic/example_SecurityOperations/pass2.yaml index a9ec6590ab9..56b31b527d5 100644 --- a/tests/openapi/checks/resource/generic/example_SecurityOperations/pass2.yaml +++ b/tests/openapi/checks/resource/generic/example_SecurityOperations/pass2.yaml @@ -1,4 +1,4 @@ -swagger: "2.0" +openapi: 3.0.0 info: title: example version: 1.0.0 @@ -27,4 +27,4 @@ paths: type: string required: - code - - message \ No newline at end of file + - message diff --git a/tests/openapi/checks/resource/generic/example_SecurityOperations/pass3.json b/tests/openapi/checks/resource/generic/example_SecurityOperations/pass3.json new file mode 100644 index 00000000000..89ed1dde57a --- /dev/null +++ b/tests/openapi/checks/resource/generic/example_SecurityOperations/pass3.json @@ -0,0 +1,55 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "example", + "version": "1.0.0", + "contact": { + "name": "contact", + "url": "https://www.google.com/", + "email": "user@gmail.com" + } + }, + "security": [ + { + "ApiKeyAuth": [] + } + ], + "paths": { + "/": { + "get": { + "operationId": "id", + "summary": "example", + "responses": { + "200": { + "description": "200 response", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + }, + "required": [ + "code", + "message" + ] + } + } + } + } + } + }, + "components": { + "securitySchemes": { + "ApiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "X-API-KEY" + } + } + } +} \ No newline at end of file diff --git a/tests/openapi/checks/resource/generic/example_SecurityOperations/pass3.yaml b/tests/openapi/checks/resource/generic/example_SecurityOperations/pass3.yaml new file mode 100644 index 00000000000..340957dbf32 --- /dev/null +++ b/tests/openapi/checks/resource/generic/example_SecurityOperations/pass3.yaml @@ -0,0 +1,35 @@ +openapi: "3.0.3" +info: + title: example + version: 1.0.0 + contact: + name: contact + url: https://www.google.com/ + email: user@gmail.com +security: + - ApiKeyAuth: [] +paths: + "/": + get: + operationId: id + summary: example + responses: + "200": + description: 200 response + schema: + type: object + properties: + code: + type: integer + format: int32 + message: + type: string + required: + - code + - message +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-KEY \ No newline at end of file diff --git a/tests/openapi/checks/resource/generic/test_SecurityOperations.py b/tests/openapi/checks/resource/generic/test_SecurityOperations.py index 05ce403b60b..6f677bda049 100644 --- a/tests/openapi/checks/resource/generic/test_SecurityOperations.py +++ b/tests/openapi/checks/resource/generic/test_SecurityOperations.py @@ -23,6 +23,8 @@ def test_summary(self): "/pass1.json", "/pass2.yaml", "/pass2.json", + "/pass3.yaml", + "/pass3.json", } failing_resources = { "/fail1.yaml",