Files
mono/market/bp/actions/routes.py
giles c015f3f02f Security audit: fix IDOR, add rate limiting, HMAC auth, token hashing, XSS sanitization
Critical: Add ownership checks to all order routes (IDOR fix).
High: Redis rate limiting on auth endpoints, HMAC-signed internal
service calls replacing header-presence-only checks, nh3 HTML
sanitization on ghost_sync and product import, internal auth on
market API endpoints, SHA-256 hashed OAuth grant/code tokens.
Medium: SECRET_KEY production guard, AP signature enforcement,
is_admin param removal, cart_sid validation, SSRF protection on
remote actor fetch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 13:30:27 +00:00

75 lines
2.3 KiB
Python

"""Market app action endpoints.
Exposes write operations at ``/internal/actions/<action_name>`` for
cross-app callers (blog, events) via the internal action client.
"""
from __future__ import annotations
from quart import Blueprint, g, jsonify, request
from shared.infrastructure.actions import ACTION_HEADER
from shared.services.registry import services
def register() -> Blueprint:
bp = Blueprint("actions", __name__, url_prefix="/internal/actions")
@bp.before_request
async def _require_action_header():
if not request.headers.get(ACTION_HEADER):
return jsonify({"error": "forbidden"}), 403
from shared.infrastructure.internal_auth import validate_internal_request
if not validate_internal_request():
return jsonify({"error": "forbidden"}), 403
_handlers: dict[str, object] = {}
@bp.post("/<action_name>")
async def handle_action(action_name: str):
handler = _handlers.get(action_name)
if handler is None:
return jsonify({"error": "unknown action"}), 404
try:
result = await handler()
return jsonify(result)
except Exception as exc:
import logging
logging.getLogger(__name__).exception("Action %s failed", action_name)
return jsonify({"error": str(exc)}), 500
# --- create-marketplace ---
async def _create_marketplace():
data = await request.get_json()
mp = await services.market.create_marketplace(
g.s,
data["container_type"],
data["container_id"],
data["name"],
data["slug"],
)
return {
"id": mp.id,
"container_type": mp.container_type,
"container_id": mp.container_id,
"name": mp.name,
"slug": mp.slug,
"description": mp.description,
}
_handlers["create-marketplace"] = _create_marketplace
# --- soft-delete-marketplace ---
async def _soft_delete_marketplace():
data = await request.get_json()
deleted = await services.market.soft_delete_marketplace(
g.s,
data["container_type"],
data["container_id"],
data["slug"],
)
return {"deleted": deleted}
_handlers["soft-delete-marketplace"] = _soft_delete_marketplace
return bp