Skip to content

Commit a757436

Browse files
authored
Merge pull request #1925 from GSA/main
8/21/2025 Production Deploy
2 parents 41ecf8c + 0c08b56 commit a757436

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1468
-887
lines changed

.ds.baseline

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@
257257
"filename": "tests/app/db.py",
258258
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
259259
"is_verified": false,
260-
"line_number": 90,
260+
"line_number": 91,
261261
"is_secret": false
262262
}
263263
],
@@ -374,5 +374,5 @@
374374
}
375375
]
376376
},
377-
"generated_at": "2025-07-02T18:56:01Z"
377+
"generated_at": "2025-08-12T18:08:49Z"
378378
}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ run-celery: ## Run celery, TODO remove purge for staging/prod
7070
-A run_celery.notify_celery worker \
7171
--pidfile="/tmp/celery.pid" \
7272
--loglevel=INFO \
73-
--pool=eventlet
73+
--pool=gevent
7474
--concurrency=20
7575

7676

README.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ You will need the following items:
3939
This project currently works with these major versions of the following main
4040
components:
4141

42-
- Python 3.13.x
42+
- Python 3.12.x
4343
- PostgreSQL 15.x (version 12.x is used in the hosted environments)
4444

4545
These instructions will walk you through how to set your machine up with all of
@@ -117,7 +117,7 @@ configuration after installation to get working out of the box:
117117
- [jq](https://stedolan.github.io/jq/) - for working with JSON in the command
118118
line
119119
- [git](https://git-scm.com/) - for version control management
120-
- [tfenv](https://github.com/tfutils/tfenv) - for managing
120+
- [tenv](https://github.com/tofuutils/tenv) - for managing
121121
[Terraform](https://www.terraform.io/) installations
122122
- [cf-cli@8](https://docs.cloudfoundry.org/cf-cli/install-go-cli.html) - for
123123
working with a Cloud Foundry platform (e.g., cloud.gov)
@@ -136,15 +136,19 @@ brew install jq git tfenv cloudfoundry/tap/cf-cli@8 redis vim wget
136136

137137
#### Terraform Installation
138138

139-
As a part of the installation above, you just installed `tfenv` to manage
139+
As a part of the installation above, you just installed `tenv` to manage
140140
Terraform installations. This is great, but you still need to install Terraform
141141
itself, which can be done with this command:
142142

143143
```sh
144-
tfenv install "latest:^1.7"
145-
tfenv use 1.7.x # x = the patch version installed
144+
tenv
146145
```
147146

147+
This will open a menu for you; choose Terraform, then choose the latest stable
148+
version.
149+
150+
_NOTE: This project currently uses the latest `1.12.x release of Terraform._
151+
148152
#### Python Installation
149153

150154
Now we're going to install a tool to help us manage Python versions and
@@ -174,12 +178,12 @@ session to make the changes take effect.
174178
Now we're ready to install the Python version we need with `pyenv`, like so:
175179

176180
```sh
177-
pyenv install 3.13
181+
pyenv install 3.12
178182
```
179183

180-
This will install the latest version of Python 3.13.
184+
This will install the latest version of Python 3.12.
181185

182-
_NOTE: This project currently runs on Python 3.13.x._
186+
_NOTE: This project currently runs on Python 3.12.x._
183187

184188
#### Python Dependency Installation
185189

@@ -313,10 +317,10 @@ If you're upgrading an existing project to a newer version of Python, you can
313317
follow these steps to get yourself up-to-date.
314318

315319
First, use `pyenv` to install the newer version of Python you'd like to use;
316-
we'll use `3.13` in our example here since we recently upgraded to this version:
320+
we'll use `3.12` in our example here since we recently upgraded to this version:
317321

318322
```sh
319-
pyenv install 3.13
323+
pyenv install 3.12
320324
```
321325

322326
Next, delete the virtual environment you previously had set up. If you followed

app/__init__.py

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@
99
from time import monotonic
1010

1111
from celery import Celery, Task, current_task
12-
from flask import current_app, g, has_request_context, jsonify, make_response, request
12+
from flask import (
13+
current_app,
14+
g,
15+
has_request_context,
16+
jsonify,
17+
make_response,
18+
request,
19+
)
1320
from flask.ctx import has_app_context
1421
from flask_migrate import Migrate
1522
from flask_socketio import SocketIO
@@ -294,15 +301,44 @@ def record_request_details():
294301
g.start = monotonic()
295302
g.endpoint = request.endpoint
296303

304+
@app.before_request
305+
def handle_options():
306+
if request.method == "OPTIONS":
307+
response = make_response("", 204)
308+
response.headers["Access-Control-Allow-Origin"] = "*"
309+
response.headers["Access-Control-Allow-Methods"] = (
310+
"GET, POST, PUT, DELETE, OPTIONS"
311+
)
312+
response.headers["Access-Control-Allow-Headers"] = (
313+
"Content-Type, Authorization"
314+
)
315+
response.headers["Access-Control-Max-Age"] = "3600"
316+
return response
317+
297318
@app.after_request
298319
def after_request(response):
320+
# Security headers for government compliance
299321
response.headers.add("X-Content-Type-Options", "nosniff")
322+
response.headers.add("X-Frame-Options", "DENY")
323+
response.headers.add("X-XSS-Protection", "1; mode=block")
324+
response.headers.add("Referrer-Policy", "strict-origin-when-cross-origin")
325+
response.headers.add(
326+
"Permissions-Policy", "geolocation=(), microphone=(), camera=()"
327+
)
300328

301-
# Some dynamic scan findings
329+
# CORS-related security headers
302330
response.headers.add("Cross-Origin-Opener-Policy", "same-origin")
303331
response.headers.add("Cross-Origin-Embedder-Policy", "require-corp")
304332
response.headers.add("Cross-Origin-Resource-Policy", "same-origin")
305-
response.headers.add("Cross-Origin-Opener-Policy", "same-origin")
333+
334+
if not request.path.startswith("/docs"):
335+
response.headers.add(
336+
"Content-Security-Policy", "default-src 'none'; frame-ancestors 'none';"
337+
)
338+
339+
response.headers.add(
340+
"Strict-Transport-Security", "max-age=31536000; includeSubDomains"
341+
)
306342

307343
return response
308344

@@ -352,15 +388,18 @@ def setup_sqlalchemy_events(app):
352388
with app.app_context():
353389

354390
@event.listens_for(db.engine, "connect")
355-
def connect(dbapi_connection, connection_record): # noqa
391+
def connect(dbapi_connection, connection_record):
392+
current_app.logger.debug(f"Using {dbapi_connection} {connection_record}")
356393
pass
357394

358395
@event.listens_for(db.engine, "close")
359-
def close(dbapi_connection, connection_record): # noqa
396+
def close(dbapi_connection, connection_record):
360397
pass
361398

362399
@event.listens_for(db.engine, "checkout")
363-
def checkout(dbapi_connection, connection_record, connection_proxy): # noqa
400+
def checkout(dbapi_connection, connection_record, connection_proxy):
401+
current_app.logger.debug(f"Using {dbapi_connection} {connection_proxy}")
402+
364403
try:
365404
# this will overwrite any previous checkout_at timestamp
366405
connection_record.info["checkout_at"] = time.monotonic()
@@ -401,7 +440,7 @@ def checkout(dbapi_connection, connection_record, connection_proxy): # noqa
401440
)
402441

403442
@event.listens_for(db.engine, "checkin")
404-
def checkin(dbapi_connection, connection_record): # noqa
443+
def checkin(dbapi_connection, connection_record):
405444
pass
406445

407446

@@ -428,7 +467,7 @@ def app_context(self):
428467
g.request_id = self.request_id
429468
yield
430469

431-
def on_success(self, retval, task_id, args, kwargs): # noqa
470+
def on_success(self, retval, task_id, args, kwargs):
432471
# enables request id tracing for these logs
433472
with self.app_context():
434473
elapsed_time = time.monotonic() - self.start
@@ -441,9 +480,11 @@ def on_success(self, retval, task_id, args, kwargs): # noqa
441480
)
442481
)
443482

444-
def on_failure(self, exc, task_id, args, kwargs, einfo): # noqa
483+
def on_failure(self, exc, task_id, args, kwargs, einfo):
484+
445485
# enables request id tracing for these logs
446486
with self.app_context():
487+
app.logger.debug(f"einfo is {einfo}")
447488
app.logger.exception(
448489
"Celery task {task_name} (queue: {queue_name}) failed".format(
449490
task_name=self.name,

0 commit comments

Comments
 (0)