-
Notifications
You must be signed in to change notification settings - Fork 11
Generalize ASGI Middleware used in Quart to a function #564
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
base: main
Are you sure you want to change the base?
Conversation
intercepts status codes
| context = Context(req=scope, source=self.source) | ||
|
|
||
| process_cache = get_cache() | ||
| if process_cache and process_cache.is_bypassed_ip(context.remote_address): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
process_cache.is_bypassed_ip(...) short-circuits request handling and skips context setup; avoid silent bypasses or gate them behind explicit config/audit logging.
Details
✨ AI Reasoning
The change introduces a bypass: when process_cache.is_bypassed_ip(...) is true the middleware returns early and skips setting a context or running any further request handling. This is an intentional short-circuit of request processing introduced in this PR and can silently disable security/validation logic for those IPs. It reduces visibility and may leave requests unobserved by later instrumentation.
🔧 How do I fix it?
Remove debugging statements like console.log, debugger, dd(), or logic bypasses like || true. Keep legitimate logging for monitoring and error handling.
More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
| await InternalASGIMiddleware(return_value, "quart")(scope, receive, send) | ||
|
|
||
| # Modify return_value | ||
| return_value = application_instance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parameter 'return_value' is reassigned to application_instance. Avoid reassigning function parameters; use a new local variable (e.g., new_return_value) and return that instead.
Details
✨ AI Reasoning
An after-advice wrapper function receives the original function's return value as a parameter and then assigns a new value to that parameter before returning it. Reassigning an input parameter can confuse readers about the original return value and make reasoning about the wrapper harder. This change introduced the reassignment where the wrapper replaced the incoming return value with a new callable wrapping the original behavior.
🔧 How do I fix it?
Create new local variables instead of reassigning parameters. Use different variable names to clearly distinguish between input and modified values.
More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
|
|
||
| @before | ||
| def _call(func, instance, args, kwargs): | ||
| async def _call_coroutine(func, instance, args, kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename _call_coroutine to a descriptive name (e.g., wrap_quart_call_coroutine) or add a docstring explaining it wraps Quart.call and delegates to InternalASGIMiddleware.
Details
✨ AI Reasoning
A newly introduced function named _call_coroutine is intended to wrap/replace Quart.call for coroutine-style ASGI apps, but the name doesn't describe its role (middleware wrapper, context creation, delegation). A clearer name or docstring would make intent obvious and aid maintenance.
🔧 How do I fix it?
Use descriptive verb-noun function names, add docstrings explaining the function's purpose, or provide meaningful return type hints.
More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
| """ | ||
| patch_function(m, "Quart.__call__", _call) | ||
|
|
||
| if inspect.iscoroutine(m.Quart.__call__): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using inspect.iscoroutine on Quart.call will not detect coroutine functions, making the coroutine path unreachable and always treating apps as legacy. Use inspect.iscoroutinefunction (or equivalent) for this check.
Details
✨ AI Reasoning
The conditional that decides whether Quart.call is treated as a coroutine application uses an API that checks coroutine instances instead of coroutine functions. Since the target is a function, this branch will not be taken in normal operation, so the logic for non-legacy ASGI apps will never run and everything will be treated as legacy. This is a clear control-flow bug where the intended branch is unreachable.
🔧 How do I fix it?
Trace execution paths carefully. Ensure precondition checks happen before using values, validate ranges before checking impossible conditions, and don't check for states that the code has already ruled out.
More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
| from ..sinks import on_import, patch_function, before_async, after | ||
| from aikido_zen.helpers.logging import logger | ||
|
|
||
| @before_async |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function '_call_before' name is vague and undocumented; add a clear name and short docstring explaining its role and inputs (it runs prior to ASGI app execution and invokes the InternalASGIMiddleware).
Details
✨ AI Reasoning
A new async helper function was added that is intended to run before the ASGI app is invoked and invoke InternalASGIMiddleware. The function's name and lack of docstring make its role and intent unclear to readers: it's not obvious what "_call_before" is calling before, what arguments it expects, or why it invokes InternalASGIMiddleware with instance.app. This increases cognitive load for maintainers and makes the hook behavior harder to understand.
🔧 How do I fix it?
Use descriptive verb-noun function names, add docstrings explaining the function's purpose, or provide meaningful return type hints.
More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
aikido_zen/cli/__init__.py
Outdated
| else: | ||
| os.environ["PYTHONPATH"] = preload_directory | ||
|
|
||
| print(os.environ["PYTHONPATH"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the ad-hoc print(os.environ['PYTHONPATH']); use structured logging at an appropriate level or remove before shipping.
Details
✨ AI Reasoning
A new print() call was added to application startup code to emit the PYTHONPATH. This is ad-hoc console output likely used for debugging and can leak internal paths or clutter production logs. It was introduced by the recent changes and is not guarded by a dev-only feature flag or proper logging framework.
🔧 How do I fix it?
Remove debugging statements like console.log, debugger, dd(), or logic bypasses like || true. Keep legitimate logging for monitoring and error handling.
More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
aikido_zen/cli/preload/__init__.py
Outdated
| @@ -0,0 +1,2 @@ | |||
| print("debug") | |||
| raise Exception("debug") | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the intentional raise Exception('debug') from the preload module; don't leave debugger-triggering exceptions in committed imports.
Details
✨ AI Reasoning
The preload package now immediately raises an Exception during import. This is a hard stop used for debugging or tracing, which will break normal execution for any consumer importing the package. This was introduced in the new preload module and is inappropriate for committed production code.
🔧 How do I fix it?
Remove debugging statements like console.log, debugger, dd(), or logic bypasses like || true. Keep legitimate logging for monitoring and error handling.
More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
aikido_zen/cli/__init__.py
Outdated
| Idea here is to load in the code stored in the preload_directory first, Inspired by ddtrace-run: | ||
| https://github.com/DataDog/dd-trace-py/blob/3ad335edd4a032ea53680fefbcc10f8fca0690a0/ddtrace/commands/ddtrace_run.py#L41 | ||
| """ | ||
| python_path = os.environ.get("PYTHONPATH", "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add_preload_directory mutates process-global os.environ['PYTHONPATH'], causing process-wide state to persist across requests; avoid modifying environment at runtime or document the global impact.
Details
✨ AI Reasoning
The added add_preload_directory function writes to os.environ['PYTHONPATH'] (lines 15-21). Environment variables are process-global state. Changing PYTHONPATH at runtime can unintentionally alter module resolution for the rest of the process and persist across requests/uses. This is not request-scoped data and can cause surprising cross-request behavior, especially in long-running servers. The mutation is introduced in this PR (new file additions) and therefore is a new global-state side-effect.
🔧 How do I fix it?
Avoid storing request-specific data in module-level variables. Use request-scoped variables or explicitly mark shared caches as intentional.
More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
| preload_directory = os.path.join(root_dir, "cli/preload") | ||
| add_preload_directory(preload_directory) | ||
|
|
||
| # The command to run is everything after `aikido_zen` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inline comment only restates argv slicing. Remove it or replace with a brief 'why' (rationale for passing args to the executed command).
Details
✨ AI Reasoning
A comment was added that merely restates the code's behavior (how argv is sliced). Such "what" comments add little value and may rot; prefer removing it or replacing with a brief explanation of rationale or edge-cases the slicing addresses.
🔧 How do I fix it?
Write comments that explain the purpose, reasoning, or business logic behind the code using words like 'because', 'so that', or 'in order to'.
More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
This reverts commit 7f97945.
Summary by Aikido
🚀 New Features
⚡ Enhancements
🔧 Refactors
More info