Add initial Dockerfile, Pipfile.lock, and Terraform configuration for…#153
Add initial Dockerfile, Pipfile.lock, and Terraform configuration for…#153
Conversation
… resource setup; include insecure code samples for security testing
Dependency ReviewThe following issues were found:
Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. Vulnerabilitiesdevsecops-demo/Pipfile.lockOnly included vulnerabilities with severity moderate or higher. License Issuesdevsecops-demo/Pipfile.lock
OpenSSF Scorecard
Scanned Files
|
1 similar comment
Dependency ReviewThe following issues were found:
Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. Vulnerabilitiesdevsecops-demo/Pipfile.lockOnly included vulnerabilities with severity moderate or higher. License Issuesdevsecops-demo/Pipfile.lock
OpenSSF Scorecard
Scanned Files
|
| resource "azurerm_network_security_group" "catapp-sg" { | ||
| name = "${var.prefix}-sg" | ||
| location = var.location | ||
| resource_group_name = azurerm_resource_group.myresourcegroup.name | ||
|
|
||
| security_rule { | ||
| name = "HTTP" | ||
| priority = 100 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "80" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
|
|
||
| security_rule { | ||
| name = "HTTPS" | ||
| priority = 102 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "443" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
|
|
||
| security_rule { | ||
| name = "SSH" | ||
| priority = 101 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "22" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
| } |
Check failure
Code scanning / defsec
An inbound network security rule allows traffic from /0. Error
| resource "azurerm_network_security_group" "catapp-sg" { | ||
| name = "${var.prefix}-sg" | ||
| location = var.location | ||
| resource_group_name = azurerm_resource_group.myresourcegroup.name | ||
|
|
||
| security_rule { | ||
| name = "HTTP" | ||
| priority = 100 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "80" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
|
|
||
| security_rule { | ||
| name = "HTTPS" | ||
| priority = 102 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "443" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
|
|
||
| security_rule { | ||
| name = "SSH" | ||
| priority = 101 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "22" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
| } |
Check failure
Code scanning / defsec
An inbound network security rule allows traffic from /0. Error
| resource "azurerm_network_security_group" "catapp-sg" { | ||
| name = "${var.prefix}-sg" | ||
| location = var.location | ||
| resource_group_name = azurerm_resource_group.myresourcegroup.name | ||
|
|
||
| security_rule { | ||
| name = "HTTP" | ||
| priority = 100 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "80" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
|
|
||
| security_rule { | ||
| name = "HTTPS" | ||
| priority = 102 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "443" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
|
|
||
| security_rule { | ||
| name = "SSH" | ||
| priority = 101 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "22" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
| } |
Check failure
Code scanning / defsec
An inbound network security rule allows traffic from /0. Error
| resource "azurerm_network_security_group" "catapp-sg" { | ||
| name = "${var.prefix}-sg" | ||
| location = var.location | ||
| resource_group_name = azurerm_resource_group.myresourcegroup.name | ||
|
|
||
| security_rule { | ||
| name = "HTTP" | ||
| priority = 100 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "80" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
|
|
||
| security_rule { | ||
| name = "HTTPS" | ||
| priority = 102 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "443" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
|
|
||
| security_rule { | ||
| name = "SSH" | ||
| priority = 101 | ||
| direction = "Inbound" | ||
| access = "Allow" | ||
| protocol = "Tcp" | ||
| source_port_range = "*" | ||
| destination_port_range = "22" | ||
| source_address_prefix = "*" | ||
| destination_address_prefix = "*" | ||
| } | ||
| } |
Check failure
Code scanning / defsec
SSH access should not be accessible from the Internet, should be blocked on port 22 Error
| resource "azurerm_virtual_machine" "catapp" { | ||
| name = "${var.prefix}-meow" | ||
| location = var.location | ||
| resource_group_name = azurerm_resource_group.myresourcegroup.name | ||
| vm_size = var.vm_size | ||
|
|
||
| network_interface_ids = [azurerm_network_interface.catapp-nic.id] | ||
| delete_os_disk_on_termination = "true" | ||
|
|
||
| storage_image_reference { | ||
| publisher = var.image_publisher | ||
| offer = var.image_offer | ||
| sku = var.image_sku | ||
| version = var.image_version | ||
| } | ||
|
|
||
| storage_os_disk { | ||
| name = "${var.prefix}-osdisk" | ||
| managed_disk_type = "Standard_LRS" | ||
| caching = "ReadWrite" | ||
| create_option = "FromImage" | ||
| } | ||
|
|
||
| os_profile { | ||
| computer_name = var.prefix | ||
| admin_username = var.admin_username | ||
| admin_password = var.admin_password | ||
| } | ||
|
|
||
| os_profile_linux_config { | ||
| disable_password_authentication = false | ||
| } | ||
|
|
||
| tags = {} | ||
|
|
||
| # Added to allow destroy to work correctly. | ||
| depends_on = [azurerm_network_interface_security_group_association.catapp-nic-sg-ass] | ||
| } |
Check failure
Code scanning / defsec
Password authentication should be disabled on Azure virtual machines Error
| try: | ||
| print(xs[7]) | ||
| print(xs[8]) | ||
| except: pass |
Check notice
Code scanning / CodeQL
Except block handles 'BaseException' Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 11 hours ago
In general, to fix this type of issue you replace a bare except: (or except BaseException:) with more specific exception handlers, typically except Exception: for “normal” runtime errors, and optionally separate handlers for KeyboardInterrupt and SystemExit if you need special behavior. This ensures that program termination and user interrupts are not accidentally swallowed.
For this specific file:
- At line 10, instead of
except: pass, we should catch the precise error that can occur in thetryblock. Thetryblock indexes into a list (xs[7]andxs[8]), so the realistic error isIndexError. Replacing the bareexcept:withexcept IndexError:preserves the idea of “ignoring out-of-range access” while no longer catchingKeyboardInterruptorSystemExit. - The
except: continueinside the loop on lines 14–16 is not highlighted by CodeQL in the prompt, so we will leave it unchanged to respect the instruction to only fix the specific reported issue.
No new imports or helper methods are needed; we just update the exception clause on line 10 inside devsecops-demo/insecure-01.py.
| @@ -7,7 +7,8 @@ | ||
| try: | ||
| print(xs[7]) | ||
| print(xs[8]) | ||
| except: pass | ||
| except IndexError: | ||
| pass | ||
|
|
||
| ys=[1, 2, None, None] | ||
| for y in ys: |
| try: | ||
| print(xs[7]) | ||
| print(xs[8]) | ||
| except: pass |
Check notice
Code scanning / CodeQL
Empty except Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 11 hours ago
In general, an empty except should either (a) catch only the specific exception type you expect and handle it appropriately (log, recover, or re-raise), or (b) if you truly intend to ignore it, document that explicitly in a comment and usually still narrow the exception type. Avoid bare except: because it hides programming errors like KeyboardInterrupt and SystemExit.
For this specific code, the only realistic exception from print(xs[7]); print(xs[8]) is IndexError when accessing xs[8]. The best fix that preserves existing behavior (the script continues even if the index is out of range) is:
- Narrow the handler to
except IndexError as exc:. - Log or print a short message including the exception so that failures are visible instead of silently ignored.
We only need to modify the try/except block around lines 7–10 in devsecops-demo/insecure-01.py. No extra imports are required since a simple print is enough for this demo script.
| @@ -7,7 +7,8 @@ | ||
| try: | ||
| print(xs[7]) | ||
| print(xs[8]) | ||
| except: pass | ||
| except IndexError as exc: | ||
| print(f"Index error while accessing xs: {exc}") | ||
|
|
||
| ys=[1, 2, None, None] | ||
| for y in ys: |
| for y in ys: | ||
| try: | ||
| print(str(y+3)) #TypeErrors ahead | ||
| except: continue #not how to handle them |
Check notice
Code scanning / CodeQL
Except block handles 'BaseException' Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 11 hours ago
In general, to fix “Except block directly handles BaseException”, replace bare except: with explicit except Exception: or, better, with the specific exception types you expect, and never silently swallow KeyboardInterrupt or SystemExit. If you truly must catch all exceptions, re‑raise KeyboardInterrupt and SystemExit explicitly.
For this snippet, we can preserve existing behavior (ignore only the expected runtime errors) while no longer catching BaseException:
- On lines 7–10, the code indexes into
xsbeyond its length. The only likely error isIndexError. Replaceexcept:withexcept IndexError:and keeppassto maintain the “ignore index errors” semantics. - On lines 14–16, the code adds
3to elements ofys, two of which areNone. That raisesTypeError. Replaceexcept:withexcept TypeError:and keepcontinueso the loop still skips problematic elements.
No new imports or helper functions are needed; we only tighten the exception types in devsecops-demo/insecure-01.py at the two bare except: sites.
| @@ -7,13 +7,15 @@ | ||
| try: | ||
| print(xs[7]) | ||
| print(xs[8]) | ||
| except: pass | ||
| except IndexError: | ||
| pass | ||
|
|
||
| ys=[1, 2, None, None] | ||
| for y in ys: | ||
| try: | ||
| print(str(y+3)) #TypeErrors ahead | ||
| except: continue #not how to handle them | ||
| except TypeError: | ||
| continue #not how to handle them | ||
|
|
||
| #some imports | ||
| import telnetlib |
| except: continue #not how to handle them | ||
|
|
||
| #some imports | ||
| import telnetlib |
Check notice
Code scanning / CodeQL
Unused import Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 11 hours ago
To fix an unused import, remove the import statement for the unused module while leaving all other code intact. This eliminates the unnecessary dependency and satisfies the static analysis rule without altering runtime behavior.
In this specific case, remove the line import telnetlib from devsecops-demo/insecure-01.py. Keep the import ftplib line as-is, because we are not shown whether ftplib is used elsewhere in this file, and we should not modify or infer beyond the provided snippet. No new methods, definitions, or imports are required; the fix is purely the deletion of the unused telnetlib import on line 19.
| @@ -16,7 +16,6 @@ | ||
| except: continue #not how to handle them | ||
|
|
||
| #some imports | ||
| import telnetlib | ||
| import ftplib | ||
|
|
||
| #B303 and B324 |
|
|
||
| #some imports | ||
| import telnetlib | ||
| import ftplib |
Check notice
Code scanning / CodeQL
Unused import Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 11 hours ago
To fix an unused import, the general approach is to remove the import statement for the module that is not used anywhere in the file. This reduces unnecessary dependencies and slightly improves readability and load time.
In this specific case, in devsecops-demo/insecure-01.py, line 20 (import ftplib) should be removed, because there are no references to ftplib in the file. We leave the neighboring import hashlib and import telnetlib unchanged, since hashlib is clearly used and telnetlib might be intended for future or external use (and is not the one flagged). No additional methods, imports, or definitions are required; we are only deleting the unused import line.
| @@ -17,7 +17,6 @@ | ||
|
|
||
| #some imports | ||
| import telnetlib | ||
| import ftplib | ||
|
|
||
| #B303 and B324 | ||
| s = b"I am a string" |
| @@ -0,0 +1,30 @@ | |||
|
|
|||
| from flask import request, render_template, make_response | |||
Check notice
Code scanning / CodeQL
Unused import Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 11 hours ago
To fix an unused import, remove the imported name from the import statement while keeping the needed ones. Here, we should keep request and render_template (both are used) and drop make_response.
Concretely, in devsecops-demo/routes-01.py, adjust line 2 so that make_response is no longer imported. No other code changes are required because make_response is not referenced anywhere in the shown snippet. This preserves all existing functionality while eliminating the unnecessary dependency.
| @@ -1,5 +1,5 @@ | ||
|
|
||
| from flask import request, render_template, make_response | ||
| from flask import request, render_template | ||
|
|
||
| from server.webapp import flaskapp, cursor | ||
| from server.models import Book |
| def index(): | ||
| name = request.args.get('name') | ||
| author = request.args.get('author') | ||
| read = bool(request.args.get('read')) |
Check notice
Code scanning / CodeQL
Unused local variable Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 11 hours ago
In general, an unused local variable should either be removed or renamed to make its intentional unusedness clear. Since the read value is not used anywhere and there is no sign that it must be evaluated for side effects, the safest, least invasive fix is to delete the assignment entirely.
For this specific case in devsecops-demo/routes-01.py, the best fix is to remove line 12:
read = bool(request.args.get('read'))and leave the rest of the function unchanged. This preserves all existing behavior (no code was using read), eliminates the unused variable, and removes the CodeQL warning. No additional imports or definitions are required, and all changes are confined to the index function in this file.
| @@ -9,7 +9,6 @@ | ||
| def index(): | ||
| name = request.args.get('name') | ||
| author = request.args.get('author') | ||
| read = bool(request.args.get('read')) | ||
|
|
||
| if name: | ||
| cursor.execute( |
There was a problem hiding this comment.
templateanalyzer found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
There was a problem hiding this comment.
checkov found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
| try: | ||
| print(xs[7]) | ||
| print(xs[8]) | ||
| except: pass |
Check warning
Code scanning / Bandit
Try, Except, Pass detected. Warning
| for y in ys: | ||
| try: | ||
| print(str(y+3)) #TypeErrors ahead | ||
| except: continue #not how to handle them |
Check warning
Code scanning / Bandit
Try, Except, Continue detected. Warning
|
|
||
| #B303 and B324 | ||
| s = b"I am a string" | ||
| print("MD5: " +hashlib.md5(s).hexdigest()) |
Check warning
Code scanning / Bandit
Use of weak MD5 hash for security. Consider usedforsecurity=False Warning
| #B303 and B324 | ||
| s = b"I am a string" | ||
| print("MD5: " +hashlib.md5(s).hexdigest()) | ||
| print("SHA1: " +hashlib.sha1(s).hexdigest()) |
Check warning
Code scanning / Bandit
Use of weak SHA1 hash for security. Consider usedforsecurity=False Warning
| "flask": { | ||
| "hashes": [ | ||
| "sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2", | ||
| "sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a" | ||
| ], | ||
| "index": "pypi", | ||
| "version": "==2.0.2" | ||
| }, |
Check failure
Code scanning / Trivy
flask: Possible disclosure of permanent session cookie due to missing Vary: Cookie header High
| "jinja2": { | ||
| "hashes": [ | ||
| "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45", | ||
| "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c" | ||
| ], | ||
| "markers": "python_version >= '3.6'", | ||
| "version": "==3.0.2" | ||
| }, |
Check failure
Code scanning / Trivy
jinja2: Jinja has a sandbox breakout through malicious filenames High
| "jinja2": { | ||
| "hashes": [ | ||
| "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45", | ||
| "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c" | ||
| ], | ||
| "markers": "python_version >= '3.6'", | ||
| "version": "==3.0.2" | ||
| }, |
Check failure
Code scanning / Trivy
jinja2: Jinja has a sandbox breakout through indirect reference to format method High
| "werkzeug": { | ||
| "hashes": [ | ||
| "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f", | ||
| "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a" | ||
| ], | ||
| "markers": "python_version >= '3.6'", | ||
| "version": "==2.0.2" | ||
| } |
Check failure
Code scanning / Trivy
python-werkzeug: high resource usage when parsing multipart form data with many fields High
| "werkzeug": { | ||
| "hashes": [ | ||
| "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f", | ||
| "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a" | ||
| ], | ||
| "markers": "python_version >= '3.6'", | ||
| "version": "==2.0.2" | ||
| } |
Check failure
Code scanning / Trivy
python-werkzeug: user may execute code on a developer's machine High
| "werkzeug": { | ||
| "hashes": [ | ||
| "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f", | ||
| "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a" | ||
| ], | ||
| "markers": "python_version >= '3.6'", | ||
| "version": "==2.0.2" | ||
| } |
Check warning
Code scanning / Trivy
Werkzeug: Werkzeug: Denial of service via Windows device names in path segments Medium
| "werkzeug": { | ||
| "hashes": [ | ||
| "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f", | ||
| "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a" | ||
| ], | ||
| "markers": "python_version >= '3.6'", | ||
| "version": "==2.0.2" | ||
| } |
Check warning
Code scanning / Trivy
Werkzeug safe_join() allows Windows special device names with compound extensions Medium
| "werkzeug": { | ||
| "hashes": [ | ||
| "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f", | ||
| "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a" | ||
| ], | ||
| "markers": "python_version >= '3.6'", | ||
| "version": "==2.0.2" | ||
| } |
Check warning
Code scanning / Trivy
Werkzeug safe_join() allows Windows special device names Medium
| "flask": { | ||
| "hashes": [ | ||
| "sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2", | ||
| "sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a" | ||
| ], | ||
| "index": "pypi", | ||
| "version": "==2.0.2" | ||
| }, |
Check notice
Code scanning / Trivy
flask: Flask: Information disclosure via improper caching of session data Low
| "werkzeug": { | ||
| "hashes": [ | ||
| "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f", | ||
| "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a" | ||
| ], | ||
| "markers": "python_version >= '3.6'", | ||
| "version": "==2.0.2" | ||
| } |
Check notice
Code scanning / Trivy
python-werkzeug: cookie prefixed with = can shadow unprefixed cookie Low
… resource setup; include insecure code samples for security testing