Split the monolithic generate_form f-string into form.html.j2/css/js
plus admin.js, inject a single window.CONFIG blob, and add structural
plus Playwright coverage so qty delegation and clamp stay green in CI.
* ci: add least-privilege permissions blocks to workflow callers
Resolves code scanning alerts #9 and #11 (actions/missing-workflow-permissions). Both callable workflows only need contents: read; the dependency-review callable already declares it internally, this caps the caller token to match.
* fix: turn off debug mode in Flask app configuration.
Resolves code scanning alert #2 (Flask app is run in debug mode)
* ci: bump reusable workflow pin to f71002a (ruff 0.15.22 pin)
Picks up Sea-Haven-Industries/.github#88, which pins ruff in
ci-python-sam so unpinned installs no longer float to new releases
with changed default rule sets (0.16.0 broke CI with 89 pre-existing
findings). Refs Sea-Haven-Industries/.github#87.
Moves the false-positive suppression for tests/test_submit_order.py:45 (a dummy
test API key, proof-or-kill verified 2026-07-13) from machine-level to a tracked
repo-local .security-review/suppressions.json so the Open SWE daily-report
automation — which cannot see ~/.config on the Mac — resolves it. Machine-level
copy retained until this merges.
* fix(test): mock get_settings/get_roster in aggregated tests + enable CI tests (INFRA-72)
handle_orders_aggregated now delivers the summary via admin DMs (PR #15),
adding get_settings/get_roster calls the aggregated tests never mocked, so
they hit live DynamoDB. Mock both and assert the message content on the
send_dm path. Set run-tests: true so the suite actually runs in CI.
* fix(test): default AWS region in conftest so CI collection doesn't hit NoRegionError (INFRA-72)
Handlers build boto3 clients at module load; CI runners have no AWS config,
so test collection raised NoRegionError once the suite actually ran. Set a
region default before imports (offline client construction; calls are mocked).
* fix(test): add repo root to sys.path so CI's bare pytest collects functions.* (INFRA-72)
test_aggregate_orders imports functions.aggregate_orders.handler, which needs
the repo root on sys.path. python -m pytest injects CWD automatically but CI
runs pytest directly, so these 11 tests errored at collection in CI only.
* Add CloudWatch alarm coverage for the meal-order-manager stack
Add CloudWatch alarms (all notifying the shared site-alerts SNS topic,
no OKActions, TreatMissingData notBreaching) across the stack:
- Lambda Errors + Throttles alarms for all 7 functions (Sum, 5min,
threshold 0).
- Lambda Duration p99 alarms at ~80% of each function's timeout;
API-fronted functions eval 3/3, cron/async functions eval 1/1.
Thresholds pending sign-off.
- DynamoDB orders-table Read/WriteThrottleEvents alarms (TableName dim).
ThrottledRequests/SystemErrors are not published at the table-only
dimension, so they are intentionally omitted.
- API Gateway (OrderApi v2) 5xx, 4xx (threshold 20, 3/2 to absorb
routine authorizer 401s), and p99 Latency alarms.
Update README with a Monitoring section and correct the Lambda count
to 7 (admin-authorizer was missing).
* Drop pending-sign-off wording from alarm docs
Duration/Latency thresholds are owner-approved; remove PENDING ADAM
SIGN-OFF / pending-sign-off notes from template.yaml comments and README.
aggregate_orders uploads the weekly PDF/CSVs to S3 and then calls
put_summary(), but put_summary spread the summary dict (which contains
float prices/totals) straight into put_item without Decimal conversion.
boto3 rejects floats (TypeError: Float types are not supported), so the
SUMMARY DynamoDB item was never written for any week (W20-W23).
The summary-PDF download endpoint gates on get_summary(week), so it got
None and returned 404 -- 'No summary PDF for <week> yet' -- even though
the PDF was sitting in S3.
Convert via _to_decimal in put_summary, matching put_order/put_settings.
Scope-down requirement from INFRA-97: github-cfn-execution-role
needs iam:CreateRole scoped to roles that carry the org boundary,
so every role this stack creates must declare it.
- Globals.Function.PermissionsBoundary: applies to all six
SAM auto-generated Lambda execution roles
- AdminAuthorizerInvokeRole: adds PermissionsBoundary + Path
/cfn-managed/ (explicit AWS::IAM::Role)
The only consumer of AdminAuthorizerInvokeRole is the HttpApi
authorizer's FunctionInvokeRole, which references it via
!GetAtt AdminAuthorizerInvokeRole.Arn — no hardcoded ARN
strings, so the path change is safe.
Refs: INFRA-103
The /api/admin/* routes (GET/PUT/DELETE /api/admin/orders and
GET /api/admin/summary-pdf) were AuthorizationType NONE, relying entirely on
the in-handler _verify_admin Google-token + admin-email check. This adds an
HTTP API Lambda authorizer that enforces the same check at the gateway, so
unauthenticated requests are rejected before reaching the integration.
- New admin_authorizer Lambda: validates the Authorization: Bearer Google ID
token (aud + allowed Workspace domain) and the admin_emails allow-list from
DynamoDB, returning the HTTP API simple response {isAuthorized}. Fails closed
on missing config, unavailable client ID, bad token, or DynamoDB error.
- OrderApi gains an AdminGoogleAuthorizer with result caching disabled
(AuthorizerResultTtlInSeconds: 0) so expired tokens / removed admins can't be
served from cache. Wired onto all four admin events; no DefaultAuthorizer, so
public routes (submit-order, form-status, roster) stay NONE.
- IAM role for API Gateway to invoke the authorizer; 60-day log group.
- 10 unit tests for the authorizer.
No client change: the admin panel already sends Authorization: Bearer
<google_id_token>. The in-handler _verify_admin check stays as defense-in-depth.
Cross-reviewed by GPT-4.1 (APPROVE-WITH-FIXES); both BLOCK items applied
(disable authorizer caching, fail-closed on DynamoDB error).
Address GPT-4.1 cross-review of the IAM change:
- Validate the week param (YYYY-WNN) and the DynamoDB-sourced PDF key
shape before presigning, so a tampered SUMMARY record can't mint URLs
for other report files (payroll CSVs).
- Narrow the IAM resource from reports/* to
reports/*/weekly-summary-*.pdf — least privilege over the bucket.
- Reuse a module-level S3 client; name the URL TTL constant.
- Distinguish "week not found" from "PDF key missing" 404s.
- Tests: malformed-week 400 and tampered-key 500 (asserts no presign).
The weekly summary PDF generated at Thursday close was stored in the
reports bucket with no way to reach it from the UI — admins had to pull
it from S3 manually. Surface it in the admin panel:
- submit_order: GET /api/admin/summary-pdf?week= (admin-gated) returns
a 5-minute presigned URL from the SUMMARY item's stamped PDF key;
404 for weeks that haven't closed.
- template.yaml: REPORTS_BUCKET env var + read-only s3:GetObject on
reports/* for SubmitOrderFunction (needed so the presigned URL is
signed with sufficient permissions).
- generate_form.py: "Download summary PDF" button in the admin header;
explains Thursday-close timing on 404.
- Tests: presign happy path, 404 open week, 400 missing week, 401
unauthenticated.
- README updated.
Bump aws-actions/configure-aws-credentials to @v6 (org target) in the
weekly-menu workflow. v6 is the verified org standard alongside
actions/checkout@v6.
Ref: engineering-handbook cicd.md (workflow standardization).
* Add dependency-review caller workflow
Add a pull_request-triggered caller that invokes the org-level
callable-dependency-review workflow to scan dependency changes and
fail on high-severity advisories.
* chore: retrigger checks
* chore: retrigger dep review (post-fix)
Re-adds WebACLId (from SSM /seahaven/waf/app-web-acl-arn) now that the
github-cfn-execution-role has wafv2 perms. Deployed + verified: orders.seahaven.com
distribution now fronted by seahaven-app-waf.
* Add WAF + API access logging/throttling (audit Day 3: M-17, M-18)
- M-17: associate the shared seahaven-app-waf CloudFront WebACL (ARN from SSM
/seahaven/waf/app-web-acl-arn) with the orders.seahaven.com distribution.
- M-18: enable HTTP API access logging to /aws/apigateway/meal-order-manager
(90d) + default route throttling (100 rps, 50 burst) on OrderApi.
* Defer M-17 WAF association (deploy role lacks wafv2)
The github-cfn-execution-role (sticky CFN service role on this stack) has
cloudfront:* + ssm:* but no wafv2:*, so associating the WebACL fails with
'Unable to verify read permissions on Web ACL'. Landing M-18 (access logging +
throttling) now; WAF association re-added once the deploy role gets wafv2 perms
(tracked separately).