@ti-engine/web-framework 1.15.0 → 1.16.0

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  This document will contain the list of changes made to the framework. The format is based on the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
4
4
 
5
+ ## Version 1.16.0
6
+
7
+ Support explicitly trusted request origins so state-changing requests (e.g. login) work behind proxies that do not present the app's external host — most notably GitHub Codespaces port forwarding (CA-90).
8
+
9
+ * feat(web-framework): add `TI_WEB_TRUSTED_ORIGINS` (comma-separated) / `config.trustedOrigins`. The `originRefererValidationHandler` now accepts a non-GET request whose `Origin`/`Referer` matches the server-reconstructed base URL **or** any configured trusted origin. Previously such a request behind a proxy that rewrote/omitted the forwarded host was rejected with `E_WEB_INVALID_REQUEST_PARAMETERS` (HTTP 403). Backward compatible (empty list = prior behavior); the CSRF double-submit token check is unchanged and still enforced
10
+ * build(release): bump package version from `1.15.0` to `1.16.0`
11
+
5
12
  ## Version 1.15.0
6
13
 
7
14
  A dedicated health endpoint, a `TI_WEB_AUTH_METHODS` env override, and login-page gating for every auth method — completing the container-friendly auth/health story for the competence deployment (CA-90).
package/bin/web-server.js CHANGED
@@ -301,7 +301,7 @@ class TiWebServer extends ServiceConsumer {
301
301
  store: new SessionStore()
302
302
  } ) );
303
303
  this.#webServer.use( webHandlers.csrfInitHandler( this ) );
304
- this.#webServer.use( webHandlers.originRefererValidationHandler() );
304
+ this.#webServer.use( webHandlers.originRefererValidationHandler( this ) );
305
305
  this.#webServer.use( webHandlers.csrfProtectionHandler() );
306
306
 
307
307
  // Set up the web server routes:
@@ -14,9 +14,10 @@ const tools = require( "@ti-engine/core/tools" );
14
14
  * Applies TI_WEB_* environment-variable overrides onto an (already-merged) web server configuration object.
15
15
  * Each override is applied ONLY when its environment variable is defined, so an absent variable leaves the
16
16
  * configured/default value untouched (fully backward compatible). This gives ti-engine web servers 12-factor,
17
- * container-friendly control over network binding, TLS, the session cookie secret, and the enabled authentication
18
- * methods without editing config files. Note `TI_WEB_AUTH_METHODS` fully REPLACES `auth.enabledMethods` (a clean
19
- * array replacement) rather than merging — the config-file merge is by-index and cannot cleanly override an array.
17
+ * container-friendly control over network binding, TLS, the session cookie secret, the enabled authentication
18
+ * methods, and the trusted request origins without editing config files. Note `TI_WEB_AUTH_METHODS` and
19
+ * `TI_WEB_TRUSTED_ORIGINS` fully REPLACE their config arrays (`auth.enabledMethods` / `trustedOrigins`) rather than
20
+ * merging — the config-file merge is by-index and cannot cleanly override an array.
20
21
  *
21
22
  * @method
22
23
  * @param {Object} config The web server configuration to augment (mutated in place and returned).
@@ -54,6 +55,9 @@ function applyWebConfigEnvOverrides( config, env = process.env ) {
54
55
  config.auth = config.auth || {};
55
56
  config.auth.enabledMethods = env.TI_WEB_AUTH_METHODS.split( "," ).map( ( method ) => method.trim() ).filter( ( method ) => method.length > 0 );
56
57
  }
58
+ if ( env.TI_WEB_TRUSTED_ORIGINS !== undefined ) {
59
+ config.trustedOrigins = env.TI_WEB_TRUSTED_ORIGINS.split( "," ).map( ( origin ) => origin.trim() ).filter( ( origin ) => origin.length > 0 );
60
+ }
57
61
  return config;
58
62
  }
59
63
 
@@ -690,19 +690,30 @@ module.exports.webAppHandler = ( instance ) => {
690
690
  * @returns {ExpressHandler}
691
691
  * @public
692
692
  */
693
- module.exports.originRefererValidationHandler = () => {
693
+ module.exports.originRefererValidationHandler = ( instance ) => {
694
694
  return ( request, response, next ) => {
695
695
  if ( request.method === "GET" || request.method === "HEAD" || request.method === "OPTIONS" ) {
696
696
  next();
697
697
  } else {
698
- const expectedOrigin = getBaseUrl( request );
699
698
  const providedOrigin = getRequestOrigin( request );
700
- // If the browser didnt send Origin/Referer (normal for same-origin form POSTs), let CSRF middleware handle protection instead of blocking here:
701
- if ( providedOrigin && String( providedOrigin ).trim().toLowerCase() !== String( expectedOrigin ).trim().toLowerCase() ) {
702
- logger.log( `Issue identified with origin/referer mismatch. Expected '${ expectedOrigin }', received '${ providedOrigin }'.`, logger.logSeverity.WARNING );
703
- next( exceptions.raise( exceptions.exceptionCode.E_WEB_INVALID_REQUEST_PARAMETERS, null, exceptions.httpCode.C_403 ) );
704
- } else {
699
+ // If the browser didn't send Origin/Referer (normal for same-origin form POSTs), let CSRF middleware handle protection instead of blocking here:
700
+ if ( !providedOrigin ) {
705
701
  next();
702
+ } else {
703
+ // Accept the origin the server reconstructs from the request, plus any explicitly trusted origins
704
+ // (TI_WEB_TRUSTED_ORIGINS / config.trustedOrigins). The trusted list is needed behind proxies that do
705
+ // not present the external host to the app (e.g. GitHub Codespaces port forwarding), where the browser
706
+ // Origin cannot be reconstructed from the forwarded headers.
707
+ const configured = ( instance && instance.serviceConfig && Array.isArray( instance.serviceConfig.trustedOrigins ) ) ? instance.serviceConfig.trustedOrigins : [];
708
+ const allowedOrigins = [ getBaseUrl( request ) ].concat( configured );
709
+ const normalizedProvided = String( providedOrigin ).trim().toLowerCase();
710
+ const isAllowed = allowedOrigins.some( ( origin ) => String( origin ).trim().toLowerCase() === normalizedProvided );
711
+ if ( isAllowed ) {
712
+ next();
713
+ } else {
714
+ logger.log( `Issue identified with origin/referer mismatch. Received '${ providedOrigin }'; expected one of [ ${ allowedOrigins.join( ", " ) } ].`, logger.logSeverity.WARNING );
715
+ next( exceptions.raise( exceptions.exceptionCode.E_WEB_INVALID_REQUEST_PARAMETERS, null, exceptions.httpCode.C_403 ) );
716
+ }
706
717
  }
707
718
  }
708
719
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ti-engine/web-framework",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "A web-framework based on the ti-engine. It provides a customizable ready-to-use web-server microservice and a set of tools for creating web applications. NOTICE: This is still a work in progress and the full architecture, design, and functionality are not available!",
5
5
  "author": "Boris Kostadinov <kostadinov.boris@gmail.com>",
6
6
  "license": "GPL-3.0-or-later",