-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathstart.py
More file actions
153 lines (121 loc) · 5.09 KB
/
Copy pathstart.py
File metadata and controls
153 lines (121 loc) · 5.09 KB
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import logging
import os
import sentry_sdk
from flask import Flask, make_response, render_template
from apps.globals import VERSION
from apps.root import output
from config import Config
def before_send(event, hint):
"""
Scrub sensitive data from Sentry events before sending.
This prevents passwords, API keys, and other secrets from being exposed.
"""
# List of sensitive field names to scrub (case-insensitive)
sensitive_keys = {
"password", "passwd", "pwd", "secret", "api_key", "apikey",
"token", "auth", "authorization", "credentials", "private_key",
"access_token", "refresh_token", "session", "cookie"
}
def scrub_dict(data):
"""Recursively scrub sensitive data from dictionaries."""
if not isinstance(data, dict):
return
for key in list(data.keys()):
key_lower = str(key).lower()
# Check if key contains any sensitive keyword
if any(sensitive in key_lower for sensitive in sensitive_keys):
data[key] = "[Filtered]"
elif isinstance(data[key], dict):
scrub_dict(data[key])
elif isinstance(data[key], list):
for item in data[key]:
if isinstance(item, dict):
scrub_dict(item)
# Scrub request data (including IP headers)
if "request" in event:
scrub_dict(event["request"])
if "headers" in event["request"]:
headers = event["request"]["headers"]
# Remove common IP headers
for ip_header in ["X-Forwarded-For", "X-Real-Ip", "True-Client-Ip", "Cf-Connecting-Ip"]:
if ip_header in headers:
headers[ip_header] = "[Filtered]"
# also check lowercase since headers are often lowercase
if ip_header.lower() in headers:
headers[ip_header.lower()] = "[Filtered]"
# Sometimes IP is in env
if "env" in event["request"] and "REMOTE_ADDR" in event["request"]["env"]:
event["request"]["env"]["REMOTE_ADDR"] = "[Filtered]"
# Scrub extra context
if "extra" in event:
scrub_dict(event["extra"])
# Aggressively clear user IP
if "user" not in event or event["user"] is None:
event["user"] = {}
# Explicitly set IP to a generic value so Sentry doesn't try to auto-fill it
event["user"]["ip_address"] = "0.0.0.0"
scrub_dict(event["user"])
# Scrub breadcrumbs
if "breadcrumbs" in event:
for breadcrumb in event["breadcrumbs"].get("values", []):
scrub_dict(breadcrumb)
# Scrub local variables from stack traces
if "exception" in event:
for exception in event["exception"].get("values", []):
if "stacktrace" in exception:
for frame in exception["stacktrace"].get("frames", []):
if "vars" in frame:
scrub_dict(frame["vars"])
return event
# Initialize Sentry before creating the Flask app.
# Defensive getattr() so a legacy config.py without these fields doesn't crash startup.
# SENTRY_ENVIRONMENT resolution order: Config attribute (config.py) -> env var (docker-compose) -> default.
# This lets operators tag a deployment without having to edit config.py on every node.
if getattr(Config, "SENTRY_DSN", None):
sentry_environment = (
getattr(Config, "SENTRY_ENVIRONMENT", None)
or os.environ.get("SENTRY_ENVIRONMENT")
or "local_development"
)
sentry_sdk.init(
dsn=Config.SENTRY_DSN,
environment=sentry_environment,
traces_sample_rate=getattr(Config, "SENTRY_TRACES_SAMPLE_RATE", 1.0),
# Disable default PII collection (IP, headers, cookies) for GDPR compliance
send_default_pii=False,
# Scrub sensitive data before sending
before_send=before_send,
)
app = Flask(__name__)
FMT = "[%(asctime)s] %(levelname)s [%(filename)s:%(lineno)d] [%(funcName)s] %(message)s"
LOGLEVEL = logging.INFO if os.environ.get("RUNMODE") == "production" else logging.DEBUG
logging.basicConfig(format=FMT, level=LOGLEVEL)
app.config.from_object(Config)
if app.config["RUNMODE"]:
app.logger.debug("Configuration set with RUNMODE=%s", app.config["RUNMODE"])
# ************************************************************************
# **************************** SERVICE ROUTES ****************************
# ************************************************************************
@app.route("/extent", methods=["GET", "POST"])
def extent():
return output()
@app.route("/query", methods=["GET", "POST"])
def query():
return output()
@app.route("/application.wadl")
def wadl():
template = render_template("wadl.xml")
response = make_response(template)
response.headers["Content-Type"] = "application/xml"
return response
@app.route("/version", strict_slashes=False)
def version():
response = make_response(VERSION)
response.headers["Content-Type"] = "text/plain"
return response
@app.route("/")
def doc():
return render_template("doc.html")
# **** MAIN ****
if __name__ == "__main__":
app.run()