flask to fastapi conversion#188
Conversation
Reviewer's GuideMigrates the HTTP server implementation from Flask to FastAPI/uvicorn, updates endpoints and tests to use FastAPI semantics and TestClient, adjusts template variable rendering, and refreshes Python/pydantic requirements while adding FastAPI-related dependencies. Sequence diagram for FastAPI root endpoint invoking CLIsequenceDiagram
actor Client
participant Uvicorn
participant FastAPIApp
participant CLIProcess
Client->>Uvicorn: HTTP GET /
Uvicorn->>FastAPIApp: Route request
FastAPIApp->>FastAPIApp: Parse query_params
FastAPIApp->>FastAPIApp: Build args string
FastAPIApp->>CLIProcess: subprocess.run(python cli.py args)
CLIProcess-->>FastAPIApp: stdout or CalledProcessError
alt CLI success
FastAPIApp-->>Uvicorn: PlainTextResponse(stdout)
Uvicorn-->>Client: 200 OK with surf report
else CLI error
FastAPIApp-->>Uvicorn: HTTPException 500
Uvicorn-->>Client: 500 Internal Server Error
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 security issue, 4 other issues, and left some high level feedback:
Security issues:
- Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)
General comments:
- The template setup with
Jinja2TemplatesandTEMPLATES_DIRis currently unused and the/homeroute was removed, whileindex.htmlstill referencesenv_varsandurl_for('serve_script'); either restore a corresponding FastAPI route that renders the template with the expected context or remove/adjust the template and related code to avoid dead/broken paths. - Changing
{{ env_vars|tojson }}to{{ env_vars | safe }}inindex.htmlremoves JSON encoding and may introduce XSS or JS parsing issues ifenv_varscontains unexpected characters; consider keeping JSON serialization (e.g. via a compatibletojsonfilter) rather than marking the entire value as safe. - Flask and flask-cors remain in
pyproject.tomleven though the server has been migrated to FastAPI; if Flask is no longer used elsewhere, consider removing those dependencies to keep the dependency set minimal.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The template setup with `Jinja2Templates` and `TEMPLATES_DIR` is currently unused and the `/home` route was removed, while `index.html` still references `env_vars` and `url_for('serve_script')`; either restore a corresponding FastAPI route that renders the template with the expected context or remove/adjust the template and related code to avoid dead/broken paths.
- Changing `{{ env_vars|tojson }}` to `{{ env_vars | safe }}` in `index.html` removes JSON encoding and may introduce XSS or JS parsing issues if `env_vars` contains unexpected characters; consider keeping JSON serialization (e.g. via a compatible `tojson` filter) rather than marking the entire value as safe.
- Flask and flask-cors remain in `pyproject.toml` even though the server has been migrated to FastAPI; if Flask is no longer used elsewhere, consider removing those dependencies to keep the dependency set minimal.
## Individual Comments
### Comment 1
<location path="src/server.py" line_range="54-59" />
<code_context>
+ return FileResponse(path=HELP_FILE_PATH, media_type="text/plain")
+
+
+ @app.get("/", response_class=PlainTextResponse)
+ async def default_route(request: Request):
"""Serves the surf report."""
- query_parameters = urllib.parse.parse_qsl(
- request.query_string.decode(), keep_blank_values=True
- )
parsed_parameters = [
f"{key}={value}" if value else key
- for key, value in query_parameters
+ for key, value in request.query_params.items()
]
args = ",".join(parsed_parameters)
</code_context>
<issue_to_address>
**issue (bug_risk):** Query parameter handling now drops duplicate keys compared to the previous implementation.
`urllib.parse.parse_qsl(..., keep_blank_values=True)` preserved multiple values per key (e.g. `?tag=a&tag=b`), but `request.query_params.items()` only exposes the last value, so duplicates are lost. If the CLI depends on multiple values for a key, this is a behavior change. To keep parity, use an API that preserves duplicates (e.g. `request.query_params.multi_items()`) when building `parsed_parameters`.
</issue_to_address>
### Comment 2
<location path="src/server.py" line_range="43" />
<code_context>
+ "http://127.0.0.1:8501"
+ ]
+
+ app.add_middleware(
+ CORSMiddleware,
+ allow_origins = origins,
+ allow_credentials=True,
+ allow_methods=["GET"],
+ allow_headers=["*"]
+ )
</code_context>
<issue_to_address>
**suggestion (bug_risk):** CORS is restricted to GET only, which may be too tight if non-GET endpoints are introduced.
Compared to the previous `CORS(app)` usage, this configuration will cause any future POST/PUT/DELETE routes to fail CORS preflight even though FastAPI accepts them. If you expect to add non-GET endpoints, consider either listing those methods explicitly or using a broader set of standard methods to avoid hard-to-diagnose frontend errors.
```suggestion
# Allow standard HTTP methods so future non-GET endpoints won't be blocked by CORS
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
```
</issue_to_address>
### Comment 3
<location path="src/templates/index.html" line_range="89-90" />
<code_context>
</div>
</div>
<script>
- // load .env variables from Flask server
- const env = {{ env_vars|tojson }};
+ // load .env variables from FastAPI server
+ const env = {{ env_vars | safe }};
</script>
<script src="{{ url_for('serve_script') }}"></script>
</code_context>
<issue_to_address>
**🚨 issue (security):** Switching from `tojson` to `safe` risks invalid JavaScript or injection issues.
`tojson` guarantees a valid JS literal and handles quoting/escaping for you. `env_vars | safe` assumes `env_vars` is already a correctly JSON-serialized string and disables escaping. If `env_vars` is a Python dict, it will render invalid JS (e.g. `{'key': 'val'}`) and, if it includes user-controlled data, can introduce XSS. Unless you’re passing a pre-serialized JSON string you fully control, use the FastAPI/Jinja equivalent of `tojson` (or explicit `json.dumps`) instead of `safe`.
</issue_to_address>
### Comment 4
<location path="src/templates/index.html" line_range="92" />
<code_context>
+ // load .env variables from FastAPI server
+ const env = {{ env_vars | safe }};
</script>
<script src="{{ url_for('serve_script') }}"></script>
</body>
</code_context>
<issue_to_address>
**issue (bug_risk):** Template still references `url_for('serve_script')` although the `serve_script` route has been removed.
This reference will now fail at render time, since the Flask `serve_script` route no longer exists. Please either point this to the appropriate FastAPI route (with a matching name) or change it to a static script URL that FastAPI serves correctly.
</issue_to_address>
### Comment 5
<location path="src/server.py" line_range="64-68" />
<code_context>
result = subprocess.run(
[sys.executable, str(CLI_PATH), args],
capture_output=True,
text=True,
check=True,
)
</code_context>
<issue_to_address>
**security (python.lang.security.audit.dangerous-subprocess-use-audit):** Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.
*Source: opengrep*
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| @app.get("/", response_class=PlainTextResponse) | ||
| async def default_route(request: Request): | ||
| """Serves the surf report.""" | ||
| query_parameters = urllib.parse.parse_qsl( | ||
| request.query_string.decode(), keep_blank_values=True | ||
| ) | ||
| parsed_parameters = [ | ||
| f"{key}={value}" if value else key | ||
| for key, value in query_parameters | ||
| for key, value in request.query_params.items() |
There was a problem hiding this comment.
issue (bug_risk): Query parameter handling now drops duplicate keys compared to the previous implementation.
urllib.parse.parse_qsl(..., keep_blank_values=True) preserved multiple values per key (e.g. ?tag=a&tag=b), but request.query_params.items() only exposes the last value, so duplicates are lost. If the CLI depends on multiple values for a key, this is a behavior change. To keep parity, use an API that preserves duplicates (e.g. request.query_params.multi_items()) when building parsed_parameters.
| CORSMiddleware, | ||
| allow_origins = origins, | ||
| allow_credentials=True, | ||
| allow_methods=["GET"], |
There was a problem hiding this comment.
suggestion (bug_risk): CORS is restricted to GET only, which may be too tight if non-GET endpoints are introduced.
Compared to the previous CORS(app) usage, this configuration will cause any future POST/PUT/DELETE routes to fail CORS preflight even though FastAPI accepts them. If you expect to add non-GET endpoints, consider either listing those methods explicitly or using a broader set of standard methods to avoid hard-to-diagnose frontend errors.
| allow_methods=["GET"], | |
| # Allow standard HTTP methods so future non-GET endpoints won't be blocked by CORS | |
| allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"], |
| // load .env variables from Flask server | ||
| const env = {{ env_vars|tojson }}; |
There was a problem hiding this comment.
🚨 issue (security): Switching from tojson to safe risks invalid JavaScript or injection issues.
tojson guarantees a valid JS literal and handles quoting/escaping for you. env_vars | safe assumes env_vars is already a correctly JSON-serialized string and disables escaping. If env_vars is a Python dict, it will render invalid JS (e.g. {'key': 'val'}) and, if it includes user-controlled data, can introduce XSS. Unless you’re passing a pre-serialized JSON string you fully control, use the FastAPI/Jinja equivalent of tojson (or explicit json.dumps) instead of safe.
| // load .env variables from FastAPI server | ||
| const env = {{ env_vars | safe }}; | ||
| </script> | ||
| <script src="{{ url_for('serve_script') }}"></script> |
There was a problem hiding this comment.
issue (bug_risk): Template still references url_for('serve_script') although the serve_script route has been removed.
This reference will now fail at render time, since the Flask serve_script route no longer exists. Please either point this to the appropriate FastAPI route (with a matching name) or change it to a static script URL that FastAPI serves correctly.
| result = subprocess.run( | ||
| [sys.executable, Path("src") / "cli.py", args], | ||
| [sys.executable, str(CLI_PATH), args], | ||
| capture_output=True, | ||
| text=True, | ||
| check=True, |
There was a problem hiding this comment.
security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.
Source: opengrep
Codecov Report✅ All modified and coverable lines are covered by tests.
🚀 New features to boost your workflow:
|
General:
Code:
Summary by Sourcery
Migrate the HTTP server from Flask to FastAPI while preserving existing endpoints and updating tests and dependencies accordingly.
New Features:
Enhancements:
Build:
Tests: