File size: 1,869 Bytes
4d5727a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12a6c9a
4d5727a
 
 
 
12a6c9a
 
 
4d5727a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
"""
Viewer HTML rendering helpers.

Separated from app.py so the factory stays under 150 lines.
"""

import os
import secrets
import base64
from flask import make_response


def make_viewer_response(base_dir: str):
    """
    Read src/viewer/index.html, inject nonce + version + token, set CSP headers.

    Returns a Flask Response object ready to send to the client.
    Raises an exception if the template file is missing.
    """
    template_path = os.path.join(base_dir, "viewer", "index.html")
    with open(template_path, "r", encoding="utf-8") as f:
        template = f.read()

    nonce = base64.urlsafe_b64encode(secrets.token_bytes(16)).decode("utf-8").rstrip("=")

    # D2.2: Never embed the raw AGENTCACHE_SECRET in page source.
    # Replace the placeholder with an empty string — the viewer authenticates
    # via the Authorization header set programmatically after load.
    html = (
        template
        .replace("__AGENTCACHE_VIEWER_NONCE__", nonce)
        .replace("__AGENTCACHE_VERSION__", "0.9.8")
        .replace("__AGENTCACHE_AUTO_TOKEN__", "")
    )

    csp = "; ".join([
        "default-src 'none'",
        "base-uri 'none'",
        "frame-ancestors 'self' https://huggingface.co https://*.hf.space",
        "object-src 'none'",
        "form-action 'none'",
        f"script-src 'nonce-{nonce}'",
        "script-src-attr 'none'",
        "style-src 'unsafe-inline'",
        (
            "connect-src 'self' https: http://localhost:* http://127.0.0.1:* "
            "wss: ws://localhost:* ws://127.0.0.1:* wss://localhost:* wss://127.0.0.1:*"
        ),
        "img-src 'self' data:",
        "font-src 'self'",
    ])

    res = make_response(html)
    res.headers["Content-Type"] = "text/html; charset=utf-8"
    res.headers["Content-Security-Policy"] = csp
    res.headers["Cache-Control"] = "no-cache"
    return res