csrd-utils 0.5.2__tar.gz → 0.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/PKG-INFO +1 -1
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/pyproject.toml +1 -1
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/audit.py +128 -2
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/operations.py +5 -4
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/renderer.py +9 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/gateway/middleware/auth_guard.py.template +0 -3
- csrd_utils-0.5.3/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/Dockerfile +11 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -3
- csrd_utils-0.5.2/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -7
- csrd_utils-0.5.2/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/models/health.py +0 -10
- csrd_utils-0.5.2/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/tests/test_health.py +0 -7
- csrd_utils-0.5.2/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/views/health_view.py +0 -11
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/.gitignore +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/README.md +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/AGENTS.md +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/__main__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/augments.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/git.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/infra.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/loader.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/presets.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/scaffolder.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/service_renderers.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/compose/yaml_editor.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/doctor.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/generate/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/generate/handlers.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/generate/helpers.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/generate/menu.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/models/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/models/base.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/models/spec.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/models/types.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/auth-passthrough/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/auth-passthrough/delegates/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/auth-passthrough/delegates/auth_delegate.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/auth-passthrough/dependencies/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/auth-passthrough/dependencies/auth_passthrough.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/auth-passthrough/models/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/auth-passthrough/models/auth_passthrough.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/auth-passthrough/views/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/auth-passthrough/views/auth_passthrough_view.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/caching/dependencies/cache.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/caching/views/cache_view.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/celery-dispatcher/celery_client.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/celery-dispatcher/models/an/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/celery-dispatcher/models/an/tasks.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/celery-dispatcher/models/tasks.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/celery-dispatcher/views/tasks_view.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/celery-worker/celery_app.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/celery-worker/tasks/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/celery-worker/tasks/example.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/crud-scaffold/dependencies/${entity_name_snake}_repository.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/crud-scaffold/migrations_${entity_name_snake}.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/crud-scaffold/models/${entity_name_snake}.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/crud-scaffold/repositories/${entity_name_snake}_repository.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/crud-scaffold/views/${entity_name_snake}_view.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/db-config/dependencies/db.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/gateway/dependencies/proxy.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/gateway/views/proxy_view.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-consumer/dependencies/auth.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/AUTH.md +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/auth.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/db.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/dependencies/token_service.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/migrations.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/models/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/models/auth.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/models/jwks.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/models/users.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/repositories/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/repositories/user_repository.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/services/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/services/token_service.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/views/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/views/auth_view.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/views/jwks_view.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/views/users_admin_view.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/views/users_view.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/metrics/middleware/metrics.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/metrics/views/metrics_view.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/rabbit-messaging/dependencies/rabbit.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/rabbit-messaging/handlers/example_handler.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/rabbit-messaging/handlers/ping_handler.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/rabbit-messaging/views/messaging_view.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/tracing/middleware/tracing.py.template +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/service/cookiecutter.json +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/tui_wizard/__init__.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/tui_wizard/exceptions.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/tui_wizard/menu.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/tui_wizard/models.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/tui_wizard/prompts.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/tui_wizard/terminal.py +0 -0
- {csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/tui_wizard/wizard.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csrd-utils
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
4
4
|
Summary: CLI utilities for csrd service generation and feature augmentation
|
|
5
5
|
Project-URL: Repository, https://github.com/csrd-api/fastapi-common
|
|
6
6
|
Project-URL: Documentation, https://github.com/csrd-api/fastapi-common/tree/main/packages/utils
|
|
@@ -39,6 +39,13 @@ class AuditReport:
|
|
|
39
39
|
|
|
40
40
|
_WEAK_PASSWORDS = r"(?:change_me|change_me_root|app|sandbox|password|root)"
|
|
41
41
|
_WEAK_IDENTIFIERS = r"(?:service_user|service_db|app|sandbox)"
|
|
42
|
+
_WEAK_JWT_SECRETS = r"(?:changeme[^\s]*|secret|test|jwt[_-]?secret|please[_-]?change)"
|
|
43
|
+
|
|
44
|
+
# Infra ports that should not be host-bound in production
|
|
45
|
+
_INFRA_PORT_PATTERNS = re.compile(
|
|
46
|
+
r'"(?:0\.0\.0\.0:)?(\d+):(\d+)"',
|
|
47
|
+
)
|
|
48
|
+
_SENSITIVE_INFRA_PORTS = {"5432", "3306", "6379", "5672", "15672", "27017"}
|
|
42
49
|
|
|
43
50
|
|
|
44
51
|
def _scan_file(path: Path, root: Path) -> list[AuditFinding]:
|
|
@@ -150,6 +157,114 @@ def _scan_file(path: Path, root: Path) -> list[AuditFinding]:
|
|
|
150
157
|
)
|
|
151
158
|
)
|
|
152
159
|
|
|
160
|
+
# ── New rules (M3 expansion) ──────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
# Weak JWT secret
|
|
163
|
+
jwt_hits = re.findall(
|
|
164
|
+
rf"JWT_SECRET\s*[:=]\s*['\"]?({_WEAK_JWT_SECRETS})['\"]?",
|
|
165
|
+
text,
|
|
166
|
+
flags=re.IGNORECASE,
|
|
167
|
+
)
|
|
168
|
+
if jwt_hits:
|
|
169
|
+
findings.append(
|
|
170
|
+
AuditFinding(
|
|
171
|
+
rule="weak-jwt-secret",
|
|
172
|
+
severity="high",
|
|
173
|
+
message="JWT signing secret uses a weak or default value.",
|
|
174
|
+
file=rel,
|
|
175
|
+
evidence=jwt_hits[0],
|
|
176
|
+
remediation='Generate a strong random secret: python -c "import secrets; print(secrets.token_urlsafe(48))"',
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Weak RabbitMQ password
|
|
181
|
+
rabbit_pw_hits = re.findall(
|
|
182
|
+
rf"RABBITMQ_(?:DEFAULT_)?PASS(?:WORD)?\s*[:=]\s*['\"]?({_WEAK_PASSWORDS})['\"]?",
|
|
183
|
+
text,
|
|
184
|
+
flags=re.IGNORECASE,
|
|
185
|
+
)
|
|
186
|
+
if rabbit_pw_hits:
|
|
187
|
+
findings.append(
|
|
188
|
+
AuditFinding(
|
|
189
|
+
rule="weak-rabbitmq-password",
|
|
190
|
+
severity="high",
|
|
191
|
+
message="RabbitMQ password uses a weak default.",
|
|
192
|
+
file=rel,
|
|
193
|
+
evidence=rabbit_pw_hits[0],
|
|
194
|
+
remediation="Set a strong RABBITMQ_PASSWORD via environment configuration.",
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Redis URL without authentication
|
|
199
|
+
redis_no_auth = re.findall(
|
|
200
|
+
r"(?:REDIS_URL|BROKER_URL|CELERY_BROKER_URL|RESULT_BACKEND)\s*[:=]\s*['\"]?(redis://[^:@\s]*:\d+)",
|
|
201
|
+
text,
|
|
202
|
+
flags=re.IGNORECASE,
|
|
203
|
+
)
|
|
204
|
+
if redis_no_auth:
|
|
205
|
+
findings.append(
|
|
206
|
+
AuditFinding(
|
|
207
|
+
rule="redis-no-auth",
|
|
208
|
+
severity="medium",
|
|
209
|
+
message="Redis URL has no password — any network client can read/write data.",
|
|
210
|
+
file=rel,
|
|
211
|
+
evidence=redis_no_auth[0],
|
|
212
|
+
remediation="Add requirepass to Redis and use redis://:PASSWORD@host:port in URLs.",
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Exposed infra ports in docker-compose (only scan compose files)
|
|
217
|
+
if path.name in ("docker-compose.yml", "docker-compose.yaml"):
|
|
218
|
+
for match in _INFRA_PORT_PATTERNS.finditer(text):
|
|
219
|
+
host_port = match.group(1)
|
|
220
|
+
if host_port in _SENSITIVE_INFRA_PORTS:
|
|
221
|
+
# Check if bound to 127.0.0.1 — that's acceptable
|
|
222
|
+
full_match = match.group(0)
|
|
223
|
+
if "127.0.0.1" not in full_match:
|
|
224
|
+
findings.append(
|
|
225
|
+
AuditFinding(
|
|
226
|
+
rule="exposed-infra-port",
|
|
227
|
+
severity="medium",
|
|
228
|
+
message=f"Infrastructure port {host_port} is bound to all interfaces.",
|
|
229
|
+
file=rel,
|
|
230
|
+
evidence=full_match,
|
|
231
|
+
remediation="Bind to 127.0.0.1 for dev or remove host mapping for prod.",
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Debug mode enabled
|
|
236
|
+
debug_hits = re.findall(
|
|
237
|
+
r"(?:^|\n)\s*DEBUG\s*[:=]\s*['\"]?(?:true|1|yes)['\"]?",
|
|
238
|
+
text,
|
|
239
|
+
flags=re.IGNORECASE,
|
|
240
|
+
)
|
|
241
|
+
if debug_hits:
|
|
242
|
+
findings.append(
|
|
243
|
+
AuditFinding(
|
|
244
|
+
rule="debug-mode-enabled",
|
|
245
|
+
severity="medium",
|
|
246
|
+
message="Debug mode is enabled — may expose stack traces and internal state.",
|
|
247
|
+
file=rel,
|
|
248
|
+
evidence="DEBUG=true",
|
|
249
|
+
remediation="Set DEBUG=false in production environments.",
|
|
250
|
+
)
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Shell-form CMD in Dockerfiles (no signal forwarding)
|
|
254
|
+
if path.name.startswith("Dockerfile"):
|
|
255
|
+
cmd_hits = re.findall(r"^CMD\s+(?!exec\b)(?!\[)(.+)$", text, flags=re.MULTILINE)
|
|
256
|
+
if cmd_hits:
|
|
257
|
+
findings.append(
|
|
258
|
+
AuditFinding(
|
|
259
|
+
rule="shell-form-cmd",
|
|
260
|
+
severity="medium",
|
|
261
|
+
message="Dockerfile CMD uses shell form without exec — no signal forwarding.",
|
|
262
|
+
file=rel,
|
|
263
|
+
evidence=f"CMD {cmd_hits[0][:60]}",
|
|
264
|
+
remediation="Use 'CMD exec ...' or CMD in JSON array form for proper signal handling.",
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
|
|
153
268
|
return findings
|
|
154
269
|
|
|
155
270
|
|
|
@@ -172,12 +287,23 @@ def run_audit(service_root: Path) -> AuditReport:
|
|
|
172
287
|
return AuditReport(ok=False, findings=findings)
|
|
173
288
|
|
|
174
289
|
candidates: list[Path] = []
|
|
175
|
-
include_names = {
|
|
290
|
+
include_names = {
|
|
291
|
+
"docker-compose.yml",
|
|
292
|
+
"docker-compose.yaml",
|
|
293
|
+
".env",
|
|
294
|
+
".env.example",
|
|
295
|
+
"README.md",
|
|
296
|
+
"settings.py",
|
|
297
|
+
}
|
|
176
298
|
|
|
177
299
|
for path in root.rglob("*"):
|
|
178
300
|
if not path.is_file():
|
|
179
301
|
continue
|
|
180
|
-
if
|
|
302
|
+
if (
|
|
303
|
+
path.name in include_names
|
|
304
|
+
or path.name.startswith("Dockerfile")
|
|
305
|
+
or path.suffix in {".py", ".yml", ".yaml", ".env", ".md"}
|
|
306
|
+
):
|
|
181
307
|
candidates.append(path)
|
|
182
308
|
|
|
183
309
|
seen: set[tuple[str, str, str]] = set()
|
|
@@ -72,11 +72,12 @@ def render_workspace(output_dir: Path, *, git_init: bool = False) -> Path:
|
|
|
72
72
|
env_example_content = _render_env_example(spec)
|
|
73
73
|
(output_dir / ".env.example").write_text(env_example_content, encoding="utf-8")
|
|
74
74
|
|
|
75
|
-
# Seed .env from .env.example so services can
|
|
76
|
-
#
|
|
77
|
-
#
|
|
75
|
+
# Seed .env from .env.example only on first render so services can
|
|
76
|
+
# start immediately. On subsequent renders the user may have set
|
|
77
|
+
# strong passwords or API keys — never overwrite those.
|
|
78
78
|
env_path = output_dir / ".env"
|
|
79
|
-
env_path.
|
|
79
|
+
if not env_path.exists():
|
|
80
|
+
env_path.write_text(env_example_content, encoding="utf-8")
|
|
80
81
|
(output_dir / "README.md").write_text(_render_readme(spec), encoding="utf-8")
|
|
81
82
|
(output_dir / ".gitignore").write_text(_render_gitignore(), encoding="utf-8")
|
|
82
83
|
(output_dir / ".dockerignore").write_text(_render_dockerignore(), encoding="utf-8")
|
|
@@ -614,7 +614,16 @@ def _render_gateway_routes(spec: ComposeSpec) -> str | None:
|
|
|
614
614
|
if not routes:
|
|
615
615
|
return None
|
|
616
616
|
|
|
617
|
+
gateway = next(s for s in spec.services if s.role == "gateway")
|
|
618
|
+
|
|
617
619
|
lines = ["# Gateway route table — auto-generated, but safe to edit manually.", "routes:"]
|
|
620
|
+
|
|
621
|
+
# Health probe must bypass the JWT guard so Docker HEALTHCHECK and
|
|
622
|
+
# external load balancers can reach it without credentials.
|
|
623
|
+
lines.append(' - prefix: "/_info/health"')
|
|
624
|
+
lines.append(f' target: "http://{gateway.name}:{gateway.port}"')
|
|
625
|
+
lines.append(" public: true")
|
|
626
|
+
|
|
618
627
|
for route in routes:
|
|
619
628
|
lines.append(f' - prefix: "{route["prefix"]}"')
|
|
620
629
|
lines.append(f' target: "{route["target"]}"')
|
|
@@ -60,9 +60,6 @@ class GatewayAuthMiddleware(BaseHTTPMiddleware):
|
|
|
60
60
|
if path.startswith(prefix):
|
|
61
61
|
return await call_next(request)
|
|
62
62
|
|
|
63
|
-
# Allow health/actuator endpoints on the gateway itself
|
|
64
|
-
if path in ("/health", "/actuator/health", "/actuator/info"):
|
|
65
|
-
return await call_next(request)
|
|
66
63
|
|
|
67
64
|
# Validate JWT
|
|
68
65
|
auth_header = request.headers.get("authorization", "")
|
csrd_utils-0.5.3/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/Dockerfile
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
FROM python:3.12-slim AS base
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
COPY ./src/{{cookiecutter.__service_name_snake}}/requirements.txt /app/requirements.txt
|
|
4
|
+
RUN pip install --no-cache-dir -r /app/requirements.txt
|
|
5
|
+
COPY ./src/{{cookiecutter.__service_name_snake}} /app/{{cookiecutter.__service_name_snake}}
|
|
6
|
+
RUN adduser --system --no-create-home appuser
|
|
7
|
+
USER appuser
|
|
8
|
+
ENV PORT={{ cookiecutter.port }}
|
|
9
|
+
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s --retries=3 \
|
|
10
|
+
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:${PORT}/_info/health')" || exit 1
|
|
11
|
+
CMD exec uvicorn {{cookiecutter.__service_name_snake}}:build_app --factory --host 0.0.0.0 --port ${PORT}
|
csrd_utils-0.5.2/src/csrd_utils/templates/service/{{cookiecutter.__service_name_snake}}/Dockerfile
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
FROM python:3.12-slim
|
|
2
|
-
WORKDIR /app
|
|
3
|
-
COPY ./src/{{cookiecutter.__service_name_snake}}/requirements.txt /app/requirements.txt
|
|
4
|
-
RUN pip install --no-cache-dir -r /app/requirements.txt
|
|
5
|
-
COPY ./src/{{cookiecutter.__service_name_snake}} /app/{{cookiecutter.__service_name_snake}}
|
|
6
|
-
ENV PORT={{ cookiecutter.port }}
|
|
7
|
-
CMD uvicorn {{cookiecutter.__service_name_snake}}:build_app --factory --host 0.0.0.0 --port ${PORT}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
from fastapi import APIRouter
|
|
2
|
-
|
|
3
|
-
from ..models.health import HealthResponse
|
|
4
|
-
|
|
5
|
-
router = APIRouter(tags=["Health"])
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@router.get("/health", response_model=HealthResponse)
|
|
10
|
-
def health() -> HealthResponse:
|
|
11
|
-
return HealthResponse(status="ok", service="{{ cookiecutter.service_name }}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/auth-passthrough/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.5.2 → csrd_utils-0.5.3}/src/csrd_utils/templates/augments/jwt-auth-provider/AUTH.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|