# PYTHON-PYRAMID-SEC-002: Pyramid Direct Response XSS

> **Severity:** HIGH | **CWE:** CWE-79 | **OWASP:** A03:2021

- **Language:** Python
- **Category:** Pyramid
- **URL:** https://codepathfinder.dev/registry/python/pyramid/PYTHON-PYRAMID-SEC-002
- **Detection:** `pathfinder scan --ruleset python/PYTHON-PYRAMID-SEC-002 --project .`

## Description

This rule traces user input from Pyramid request parameters (request.params,
request.GET, request.POST, request.matchdict, request.json_body) directly into
Pyramid Response() constructors. When user input is placed into an HTML response
body without escaping, it creates a reflected XSS vulnerability.

Unlike Pyramid's template rendering (Chameleon, Jinja2, Mako) which auto-escapes
by default, constructing a Response() directly with string concatenation bypasses
all template-level escaping. An attacker can inject script tags through request
parameters that execute in the victim's browser.

The rule uses taint analysis with scope="local" to track data flow within
individual view functions. It recognizes escape() and markupsafe.escape() as
sanitizers.


## Vulnerable Code

```python
from pyramid.config import Configurator
from pyramid.response import Response

# SEC-002: Direct response XSS
def vulnerable_view(request):
    name = request.params.get('name')
    return Response(f"Hello {name}")
```

## Secure Code

```python
from pyramid.response import Response
from markupsafe import escape

def search_view(request):
    query = request.params.get('q', '')
    # SECURE: Escape user input before including in HTML
    safe_query = escape(query)
    return Response(f'<h1>Results for: {safe_query}</h1>')

# BETTER: Use Pyramid templates with auto-escaping
def search_view_template(request):
    return {'query': request.params.get('q', '')}

```

## Detection Rule (Python SDK)

```python
from rules.python_decorators import python_rule
from codepathfinder import calls, flows, QueryType
from codepathfinder.presets import PropagationPresets

class PyramidResponse(QueryType):
    fqns = ["pyramid.response.Response", "pyramid.request.Response"]

_PYRAMID_SOURCES = [
    calls("request.params.get"),
    calls("request.params"),
    calls("request.GET.get"),
    calls("request.POST.get"),
    calls("request.matchdict.get"),
    calls("request.json_body.get"),
    calls("*.params.get"),
    calls("*.params"),
]


@python_rule(
    id="PYTHON-PYRAMID-SEC-002",
    name="Pyramid Direct Response XSS",
    severity="HIGH",
    category="pyramid",
    cwe="CWE-79",
    tags="python,pyramid,xss,response,OWASP-A03,CWE-79",
    message="User input flows directly to Response(). Use templates with auto-escaping.",
    owasp="A03:2021",
)
def detect_pyramid_response_xss():
    """Detects request data flowing to Pyramid Response."""
    return flows(
        from_sources=_PYRAMID_SOURCES,
        to_sinks=[
            PyramidResponse.method("__init__"),
            calls("Response"),
            calls("pyramid.response.Response"),
        ],
        sanitized_by=[
            calls("escape"),
            calls("markupsafe.escape"),
        ],
        propagates_through=PropagationPresets.standard(),
        scope="local",
    )
```

## How to Fix

- Use Pyramid's template renderers (Chameleon, Jinja2, Mako) instead of constructing Response() with string concatenation
- If you must build HTML in view code, escape all user input with markupsafe.escape() before including it
- Set Content-Type to application/json for API responses to prevent browsers from rendering HTML
- Implement Content-Security-Policy headers to limit the damage of any XSS that slips through
- Use the @view_config(renderer='templates/search.pt') pattern to separate view logic from HTML rendering

## Security Implications

- **Reflected Cross-Site Scripting:** An attacker crafts a URL with malicious JavaScript in a query parameter.
When the user clicks the link, the Pyramid view constructs a Response with
the unsanitized parameter value, and the script executes in the user's browser
with full access to their session cookies and DOM.

- **Session Hijacking:** Injected JavaScript can read document.cookie and send it to an attacker-controlled
server. The attacker then uses the stolen session cookie to impersonate the user.

- **Credential Theft:** XSS can overlay a fake login form on the page. The user sees what looks like a
legitimate login prompt, enters their credentials, and the attacker captures them.


## FAQ

**Q: Do Pyramid templates auto-escape by default?**

Yes. Chameleon (the default Pyramid template engine) and Jinja2 both
auto-escape HTML by default. This rule only fires when you bypass templates
and construct Response() objects directly with user input.


**Q: Is setting Content-Type to text/plain enough to prevent XSS?**

It helps but is not sufficient. Some older browsers may still render HTML
in text/plain responses. The correct fix is to escape user input with
markupsafe.escape() or use template rendering with auto-escaping enabled.


**Q: Does this rule detect stored XSS?**

No. This rule detects reflected XSS where user input flows directly from
request parameters to the response within a single view function. Stored
XSS requires tracking data through database writes and reads, which needs
scope="global" analysis.


**Q: Why does the rule use scope="local" instead of scope="global"?**

Pyramid view functions typically receive request data and return a response
within the same function. scope="local" covers this common pattern efficiently.


**Q: How do I run this rule in CI/CD?**

Run: pathfinder ci --ruleset python/pyramid --project .


## References

- [CWE-79: Cross-site Scripting (XSS)](https://cwe.mitre.org/data/definitions/79.html)
- [Pyramid Security Documentation](https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/security.html)
- [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Scripting_Prevention_Cheat_Sheet.html)
- [OWASP A03:2021 Injection](https://owasp.org/Top10/A03_2021-Injection/)
- [MarkupSafe Documentation](https://markupsafe.palletsprojects.com/)

---

Source: https://codepathfinder.dev/registry/python/pyramid/PYTHON-PYRAMID-SEC-002
Code Pathfinder — Open source, type-aware SAST with cross-file dataflow analysis
