@abtnode/router-provider 1.16.49-beta-20250827-025603-2bb1a7ee → 1.16.49-beta-20250828-131156-98768a61

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.
@@ -0,0 +1,3 @@
1
+ # Security headers for HTTPS responses
2
+ # Note: HSTS must only be sent on HTTPS
3
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
@@ -26,6 +26,10 @@ const {
26
26
  GATEWAY_RATE_LIMIT_GLOBAL,
27
27
  GATEWAY_RATE_LIMIT,
28
28
  DOMAIN_FOR_IP_SITE_REGEXP,
29
+ CSP_OFFICIAL_SOURCES,
30
+ CSP_SYSTEM_SOURCES,
31
+ CSP_THIRD_PARTY_SOURCES,
32
+ CSP_ICONIFY_SOURCES,
29
33
  } = require('@abtnode/constant');
30
34
  const { toHex } = require('@ocap/util');
31
35
  const promiseRetry = require('promise-retry');
@@ -164,6 +168,7 @@ class NginxProvider extends BaseProvider {
164
168
 
165
169
  this._copyConfigFiles();
166
170
  this._ensureDhparam();
171
+ this._ensureDaemonSecurityHeaders();
167
172
  this.updateProxyPolicy({ enabled: false });
168
173
  this.initialize();
169
174
  }
@@ -303,6 +308,7 @@ class NginxProvider extends BaseProvider {
303
308
  // if match certificate, then add https server
304
309
  this._addHttpsServer({
305
310
  conf,
311
+ serviceType: site.serviceType,
306
312
  locations: rules,
307
313
  certificateFileName: certificate.domain,
308
314
  serverName: parsedServerName,
@@ -314,6 +320,7 @@ class NginxProvider extends BaseProvider {
314
320
  } else {
315
321
  this._addHttpServer({
316
322
  conf,
323
+ serviceType: site.serviceType,
317
324
  locations: rules,
318
325
  serverName: parsedServerName,
319
326
  corsAllowedOrigins,
@@ -581,6 +588,16 @@ class NginxProvider extends BaseProvider {
581
588
  }
582
589
  }
583
590
 
591
+ _addSecurityHeaders(location, serviceType) {
592
+ if (serviceType === 'daemon') {
593
+ if (fs.existsSync(path.join(this.includesDir, 'daemon', 'security'))) {
594
+ location._add('include', 'includes/daemon/security');
595
+ }
596
+
597
+ location._add('include', 'includes/daemon/ssl');
598
+ }
599
+ }
600
+
584
601
  /**
585
602
  * Returns:
586
603
  * server /flash/ {
@@ -605,6 +622,7 @@ class NginxProvider extends BaseProvider {
605
622
  commonHeaders,
606
623
  cacheGroup,
607
624
  pageGroup,
625
+ serviceType,
608
626
  }) {
609
627
  server._add('location', concatPath(prefix, suffix, root));
610
628
 
@@ -637,6 +655,8 @@ class NginxProvider extends BaseProvider {
637
655
  location._add('include', 'includes/cache');
638
656
  }
639
657
 
658
+ this._addSecurityHeaders(location, serviceType);
659
+
640
660
  // Redirect blocklet traffic
641
661
  if (type === ROUTING_RULE_TYPES.BLOCKLET) {
642
662
  // FIXME: logic related to server gateway should not in provider
@@ -680,7 +700,7 @@ class NginxProvider extends BaseProvider {
680
700
  location._add('proxy_pass', `http://${getUpstreamName(port)}`);
681
701
  }
682
702
 
683
- _addRedirectTypeLocation({ server, url, redirectCode, prefix, suffix }) {
703
+ _addRedirectTypeLocation({ server, url, redirectCode, prefix, suffix, serviceType }) {
684
704
  const cleanUrl = trimEndSlash(url);
685
705
  server._add('location', `${concatPath(prefix, suffix)}`);
686
706
  const location = this._getLastLocation(server);
@@ -694,6 +714,8 @@ class NginxProvider extends BaseProvider {
694
714
  // always allow cors here since we are doing a redirect
695
715
  location._add('include', 'includes/cors');
696
716
 
717
+ this._addSecurityHeaders(location, serviceType);
718
+
697
719
  // 如果 prefix 是根路径,则不需要重写,直接将当前的请求附加到设置的重定向地址后面
698
720
  if (prefix === '/') {
699
721
  location._add('return', `${redirectCode} ${cleanUrl}$request_uri`);
@@ -704,24 +726,28 @@ class NginxProvider extends BaseProvider {
704
726
  }
705
727
  }
706
728
 
707
- _addRewriteTypeLocation({ server, url, prefix, suffix }) {
729
+ _addRewriteTypeLocation({ server, url, prefix, suffix, serviceType }) {
708
730
  server._add('location', concatPath(prefix, suffix));
709
731
  const location = this._getLastLocation(server);
732
+
733
+ this._addSecurityHeaders(location, serviceType);
710
734
  location._add('rewrite', `^${prefix}(.*) ${url}$1 last`);
711
735
  }
712
736
 
713
- _addNotFoundLocation({ server, prefix, suffix }) {
737
+ _addNotFoundLocation({ server, prefix, suffix, serviceType }) {
714
738
  server._add('location', concatPath(prefix, suffix));
715
739
  const location = this._getLastLocation(server);
716
- this._addTailSlashRedirection(location, prefix);
717
740
 
741
+ this._addSecurityHeaders(location, serviceType);
742
+ this._addTailSlashRedirection(location, prefix);
718
743
  location._add('try_files', '$uri /404.html break');
719
744
  }
720
745
 
721
- _addGeneralProxyLocation({ server, port, prefix, suffix, blockletDid, targetPrefix }) {
746
+ _addGeneralProxyLocation({ server, port, prefix, suffix, blockletDid, targetPrefix, serviceType }) {
722
747
  server._add('location', concatPath(prefix, suffix));
723
748
  const location = this._getLastLocation(server);
724
749
  this._addCommonHeader(location);
750
+ this._addSecurityHeaders(location, serviceType);
725
751
  location._add('include', 'includes/proxy');
726
752
  if (blockletDid) {
727
753
  location._add('proxy_set_header', `X-Blocklet-Did ${blockletDid}`);
@@ -867,6 +893,61 @@ class NginxProvider extends BaseProvider {
867
893
  }
868
894
  }
869
895
 
896
+ _ensureDaemonSecurityHeaders() {
897
+ const securityFilePath = path.join(this.includesDir, 'daemon', 'security');
898
+ const cspSources = [
899
+ ...CSP_OFFICIAL_SOURCES,
900
+ ...CSP_SYSTEM_SOURCES,
901
+ ...CSP_THIRD_PARTY_SOURCES,
902
+ ...CSP_ICONIFY_SOURCES,
903
+ 'data:',
904
+ 'blob:',
905
+ '*/__blocklet__.js',
906
+ '*/.well-known/ping',
907
+ ];
908
+ const cspPolicy = `default-src 'self'; frame-ancestors 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' ${cspSources.join(' ')}; font-src 'self' data:; connect-src 'self' ${cspSources.join(' ')} */.well-known/ping; base-uri 'self'; object-src 'none'`;
909
+ const cspLine = `add_header Content-Security-Policy "${cspPolicy}" always;`;
910
+
911
+ try {
912
+ if (fs.existsSync(securityFilePath)) {
913
+ logger.info('security include file already exists', { path: securityFilePath });
914
+ return;
915
+ }
916
+
917
+ const baseContent = [
918
+ '## Global HTTP security headers (safe baseline)',
919
+ '',
920
+ '# MIME sniffing protection',
921
+ 'add_header X-Content-Type-Options "nosniff" always;',
922
+ '# --- Secure Headers Baseline ---',
923
+ '# Hide headers from upstream (in case the upstream app sets them).',
924
+ '# This prevents duplicate values which may cause overly strict policies.',
925
+ 'proxy_hide_header Content-Security-Policy;',
926
+ 'proxy_hide_header Referrer-Policy;',
927
+ 'proxy_hide_header Permissions-Policy;',
928
+ '# Referrer-Policy:',
929
+ '# Controls how much referrer information is included with requests.',
930
+ '# "strict-origin-when-cross-origin" is a balanced choice: full referrer',
931
+ '# for same-origin, origin only for cross-origin, nothing on downgrade.',
932
+ '# Use "no-referrer" if you want the strictest setting.',
933
+ 'add_header Referrer-Policy "strict-origin-when-cross-origin" always;',
934
+ 'add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=(), bluetooth=(), fullscreen=(), xr-spatial-tracking=(), magnetometer=(), gyroscope=(), accelerometer=(), browsing-topics=()" always;',
935
+ 'add_header X-Frame-Options "DENY" always;',
936
+ '# Content-Security-Policy (CSP):',
937
+ '# Mitigates XSS by restricting resource loading.',
938
+ '# This baseline only allows self-hosted resources, blocks framing,',
939
+ '# disallows <base> tag overrides, and disables legacy plugins.',
940
+ '# Adjust sources (script-src, style-src, img-src, etc.) if you need CDNs.',
941
+ cspLine,
942
+ ].join('\n');
943
+
944
+ fs.writeFileSync(securityFilePath, baseContent);
945
+ logger.info('security include file updated', { path: securityFilePath });
946
+ } catch (error) {
947
+ logger.error('Failed to update security include file', { error, path: securityFilePath });
948
+ }
949
+ }
950
+
870
951
  _addCommonResHeaders(block, headers) {
871
952
  if (!headers || Object.prototype.toString.call(headers) !== '[object Object]') {
872
953
  return;
@@ -1019,11 +1100,12 @@ class NginxProvider extends BaseProvider {
1019
1100
  daemonPort,
1020
1101
  commonHeaders,
1021
1102
  blockletDid,
1103
+ serviceType,
1022
1104
  }) {
1023
1105
  const httpServerUnit = this._addHttpServerUnit({ conf, serverName, port });
1024
1106
  this._addDefaultLocations({ server: httpServerUnit, daemonPort, serverName });
1025
1107
  // eslint-disable-next-line max-len
1026
- locations.forEach((x) => this._addReverseProxy({ server: httpServerUnit, ...x, serverName, corsAllowedOrigins, commonHeaders, blockletDid })); // prettier-ignore
1108
+ locations.forEach((x) => this._addReverseProxy({ server: httpServerUnit, ...x, serverName, corsAllowedOrigins, commonHeaders, blockletDid, serviceType })); // prettier-ignore
1027
1109
  }
1028
1110
 
1029
1111
  _addHttpsServer({
@@ -1031,6 +1113,7 @@ class NginxProvider extends BaseProvider {
1031
1113
  locations,
1032
1114
  certificateFileName,
1033
1115
  serverName,
1116
+ serviceType,
1034
1117
  corsAllowedOrigins,
1035
1118
  daemonPort,
1036
1119
  commonHeaders,
@@ -1043,7 +1126,7 @@ class NginxProvider extends BaseProvider {
1043
1126
 
1044
1127
  this._addDefaultLocations({ server: httpsServerUnit, daemonPort, serverName });
1045
1128
  // eslint-disable-next-line max-len
1046
- locations.forEach((x) => this._addReverseProxy({ server: httpsServerUnit, ...x, serverName, corsAllowedOrigins, commonHeaders, blockletDid })); // prettier-ignore
1129
+ locations.forEach((x) => this._addReverseProxy({ server: httpsServerUnit, ...x, serverName, corsAllowedOrigins, commonHeaders, blockletDid, serviceType })); // prettier-ignore
1047
1130
  }
1048
1131
 
1049
1132
  _addHttpServerUnit({ conf, serverName, port = '' }) {
package/lib/util.js CHANGED
@@ -75,6 +75,7 @@ const formatRoutingTable = (routingTable) => {
75
75
  rules: [],
76
76
  port: site.port,
77
77
  corsAllowedOrigins: site.corsAllowedOrigins,
78
+ serviceType: site.serviceType,
78
79
  };
79
80
  }
80
81
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abtnode/router-provider",
3
- "version": "1.16.49-beta-20250827-025603-2bb1a7ee",
3
+ "version": "1.16.49-beta-20250828-131156-98768a61",
4
4
  "description": "Routing engine implementations for abt node",
5
5
  "author": "polunzh <polunzh@gmail.com>",
6
6
  "homepage": "https://github.com/ArcBlock/blocklet-server#readme",
@@ -32,11 +32,11 @@
32
32
  "url": "https://github.com/ArcBlock/blocklet-server/issues"
33
33
  },
34
34
  "dependencies": {
35
- "@abtnode/constant": "1.16.49-beta-20250827-025603-2bb1a7ee",
36
- "@abtnode/db-cache": "1.16.49-beta-20250827-025603-2bb1a7ee",
37
- "@abtnode/logger": "1.16.49-beta-20250827-025603-2bb1a7ee",
38
- "@abtnode/router-templates": "1.16.49-beta-20250827-025603-2bb1a7ee",
39
- "@abtnode/util": "1.16.49-beta-20250827-025603-2bb1a7ee",
35
+ "@abtnode/constant": "1.16.49-beta-20250828-131156-98768a61",
36
+ "@abtnode/db-cache": "1.16.49-beta-20250828-131156-98768a61",
37
+ "@abtnode/logger": "1.16.49-beta-20250828-131156-98768a61",
38
+ "@abtnode/router-templates": "1.16.49-beta-20250828-131156-98768a61",
39
+ "@abtnode/util": "1.16.49-beta-20250828-131156-98768a61",
40
40
  "@arcblock/http-proxy": "^1.19.1",
41
41
  "@arcblock/is-valid-domain": "^1.0.5",
42
42
  "@ocap/util": "^1.23.1",
@@ -62,5 +62,5 @@
62
62
  "bluebird": "^3.7.2",
63
63
  "fs-extra": "^11.2.0"
64
64
  },
65
- "gitHead": "2b70eea34e8bc8546abb09d38935e8906d9586fd"
65
+ "gitHead": "2d2312333cf9dd50034609c678f2fd777e0ba25a"
66
66
  }