Skip to content

Issue 760: CORS headers#833

Open
bertrandkerres wants to merge 7 commits intoibpsa:masterfrom
bertrandkerres:760-cors-headers
Open

Issue 760: CORS headers#833
bertrandkerres wants to merge 7 commits intoibpsa:masterfrom
bertrandkerres:760-cors-headers

Conversation

@bertrandkerres
Copy link
Copy Markdown
Contributor

@bertrandkerres bertrandkerres commented Feb 28, 2026

Summary

This PR addresses issue #760 to allow calling BOPTEST from a web browser.
It adds CORS support to the web service.

CORS is configured conservatively: only origins matching localhost:<port> or
*.boptest.net are allowed by default. Additional origins can be whitelisted at
deploy time via the BOPTEST_ALLOWED_ORIGINS environment variable (comma-separated),
which is threaded through docker-compose.yml.

What's included

  • New addCorsOriginHdr middleware that appends Access-Control-Allow-Origin and
    Vary: Origin to responses for allowed origins.
  • New handleCorsPreflight middleware that responds to OPTIONS preflight requests,
    advertising the correct allowed methods, Content-Type and Authorization headers,
    and a 24-hour Max-Age cache.
  • Full CORS coverage across all public API endpoints:
    • GET /version
    • GET /testcases
    • POST /testcases/*/select (all three variants)
    • GET /status/:testid, GET /name/:testid
    • PUT /stop/:testid, PUT /initialize/:testid
    • GET|PUT /scenario/:testid, GET|PUT /step/:testid
    • GET /measurements/:testid, GET /inputs/:testid
    • POST /advance/:testid, POST /submit/:testid
    • GET /kpi/:testid
    • PUT /results/:testid, PUT /forecast/:testid
    • GET /forecast_points/:testid
  • OPTIONS preflight handlers for every non-simple request (POST, PUT, and any GET
    that may carry an Authorization header).
  • Mixed GET+PUT routes (/scenario, /step) advertise both methods in their OPTIONS
    response so either verb works from a browser.

Tests

Unit tests (service/web/server/test/cors.test.js, mocha + supertest, no Docker needed):

  • addCorsOriginHdr: header presence/absence, Vary header, env-var origin, next() always called
  • handleCorsPreflight: 204 + full headers for allowed origins, 405 for disallowed/absent origin,
    correct Allow-Methods for GET / PUT / mixed routes, pass-through for non-OPTIONS verbs
  • Origin allowlist: 8 allowed and 6 disallowed patterns (wrong scheme, no port, lookalike domains)

Run locally with:

cd service/web/server && npm install && npm test

Integration tests (service/test/test_testcase_api.py, runs against the deployed stack):

  • GET /version and GET /testcases return Access-Control-Allow-Origin for an allowed origin
  • OPTIONS /testcases returns 204 with all preflight headers including Max-Age: 86400
  • OPTIONS /testcases/:id/select returns 204 and advertises POST
  • Disallowed origin receives no CORS header

bertrandkerres and others added 7 commits December 18, 2025 19:03
* Preflight for POST and PUT
* Origin header for all non-namespaced requests
…ED_ORIGIN`.

If empty, only the defaults (localhost on all ports and *.boptest.net) are allowed
- Add OPTIONS preflight handlers for all GET endpoints that may carry
  an Authorization header (testcases, status, name, measurements,
  inputs, kpi, forecast_points)
- Add OPTIONS + addCorsOriginHdr to all three POST .../select routes
  so browser clients can start a test session
- Fix /scenario/:testid and /step/:testid OPTIONS responses to advertise
  "GET, PUT" instead of only "PUT"
- Add addCorsOriginHdr to GET /version
- Add trailing newline to middleware.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Layer 1 — Node.js unit tests (mocha + supertest, no Docker needed):
- test/cors.test.js covers addCorsOriginHdr, handleCorsPreflight, and
  the full origin allowlist (allowed and disallowed origins)
- @babel/register added to package.json so mocha can transpile ES modules
- npm test wired to run the suite; new cors-unit-tests job in
  build_test_containers.yml runs it in parallel with simulation-tests

Layer 2 — pytest integration test against the deployed stack:
- test_cors_headers() in test_testcase_api.py verifies CORS headers on
  GET /version, GET /testcases, OPTIONS /testcases preflight, OPTIONS
  /testcases/:id/select preflight, and that disallowed origins are blocked

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bertrandkerres
Copy link
Copy Markdown
Contributor Author

@dhblum Here is the PR for the CORS headers that we discussed in #820

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant