@adobe/helix-html-pipeline 5.0.12 → 5.1.1

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
@@ -1,3 +1,24 @@
1
+ ## [5.1.1](https://github.com/adobe/helix-html-pipeline/compare/v5.1.0...v5.1.1) (2023-11-09)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * append .plain.html in redirects if needed ([#452](https://github.com/adobe/helix-html-pipeline/issues/452)) ([d3035a1](https://github.com/adobe/helix-html-pipeline/commit/d3035a1f593b0ac39fd9bd2ce68c8bcb83fe06b4)), closes [#451](https://github.com/adobe/helix-html-pipeline/issues/451)
7
+ * increase test time ([336bda0](https://github.com/adobe/helix-html-pipeline/commit/336bda099dae3a3faea26c71984693a2cd3a998f))
8
+
9
+ # [5.1.0](https://github.com/adobe/helix-html-pipeline/compare/v5.0.12...v5.1.0) (2023-11-06)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **options:** use request.url.pathname ([0086a2d](https://github.com/adobe/helix-html-pipeline/commit/0086a2dd4d9b8dc6b8845dab6e7bf0f63132fbdc))
15
+
16
+
17
+ ### Features
18
+
19
+ * **domainkeys:** support arrays of domainkeys in config ([f90b3b6](https://github.com/adobe/helix-html-pipeline/commit/f90b3b6a6b67804cf8b6e24e950769cdf70313be))
20
+ * **options:** add support for domainkey challenges ([8153afc](https://github.com/adobe/helix-html-pipeline/commit/8153afc8fa49cfca20763c982cadb36f30382525))
21
+
1
22
  ## [5.0.12](https://github.com/adobe/helix-html-pipeline/compare/v5.0.11...v5.0.12) (2023-10-31)
2
23
 
3
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-html-pipeline",
3
- "version": "5.0.12",
3
+ "version": "5.1.1",
4
4
  "description": "Helix HTML Pipeline",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -53,7 +53,7 @@
53
53
  "hast-util-to-html": "9.0.0",
54
54
  "hast-util-to-string": "3.0.0",
55
55
  "hastscript": "8.0.0",
56
- "jose": "5.0.1",
56
+ "jose": "5.1.0",
57
57
  "mdast-util-to-hast": "13.0.2",
58
58
  "mdast-util-to-string": "4.0.0",
59
59
  "mime": "3.0.0",
@@ -74,13 +74,13 @@
74
74
  "@markedjs/html-differ": "4.0.2",
75
75
  "@semantic-release/changelog": "6.0.3",
76
76
  "@semantic-release/git": "10.0.1",
77
- "@semantic-release/npm": "11.0.0",
77
+ "@semantic-release/npm": "11.0.1",
78
78
  "c8": "8.0.1",
79
- "eslint": "8.52.0",
79
+ "eslint": "8.53.0",
80
80
  "eslint-import-resolver-exports": "1.0.0-beta.5",
81
81
  "eslint-plugin-header": "3.1.1",
82
82
  "eslint-plugin-import": "2.29.0",
83
- "esmock": "2.5.8",
83
+ "esmock": "2.5.9",
84
84
  "husky": "8.0.3",
85
85
  "js-yaml": "4.1.0",
86
86
  "jsdom": "22.1.0",
@@ -89,7 +89,7 @@
89
89
  "mocha": "10.2.0",
90
90
  "mocha-multi-reporters": "1.5.1",
91
91
  "mocha-suppress-logs": "0.4.1",
92
- "semantic-release": "22.0.5"
92
+ "semantic-release": "22.0.7"
93
93
  },
94
94
  "lint-staged": {
95
95
  "*.js": "eslint",
@@ -10,12 +10,70 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
  import { cleanupHeaderValue } from '@adobe/helix-shared-utils';
13
+ import { createHash } from 'crypto';
13
14
  import { PipelineResponse } from './PipelineResponse.js';
14
15
  import fetchConfigAll from './steps/fetch-config-all.js';
15
16
  import setCustomResponseHeaders from './steps/set-custom-response-headers.js';
16
17
  import fetchConfig from './steps/fetch-config.js';
17
18
  import { PipelineStatusError } from './PipelineStatusError.js';
19
+ import { getOriginalHost } from './steps/utils.js';
18
20
 
21
+ /**
22
+ * Hashes the domain and domainkey
23
+ * @param {string} domain the domain
24
+ * @param {string|string[]} domainkeys the domainkey or domainkeys
25
+ * @returns {string} the hash
26
+ */
27
+ function hashMe(domain, domainkeys) {
28
+ return (Array.isArray(domainkeys) ? domainkeys : [domainkeys]).map((dk) => {
29
+ const hash = createHash('sha256');
30
+ hash.update(domain);
31
+ hash.update(dk);
32
+ return hash.digest('hex');
33
+ }).join(' ');
34
+ }
35
+
36
+ /**
37
+ * If the request is a _rum-challenge request, then set the x-rum-challenge header
38
+ * A rum-challenge request is an OPTIONS request to any path ending with _rum-challenge
39
+ * and will return a 204 response with the x-rum-challenge header set to the hash of
40
+ * the x-forwarded-host and the domainkey. If no domainkey has been set in .helix/config
41
+ * then the `slack` channel will be used instead.
42
+ * @param {object} state current pipeline state
43
+ * @param {Request} request HTTP request
44
+ * @param {Response} response HTTP response
45
+ * @returns {void}
46
+ */
47
+ function setDomainkeyHeader(state, request, response) {
48
+ // nope out if path does not end with _rum-challenge
49
+ if (!request.url.pathname.endsWith('/_rum-challenge')) {
50
+ return;
51
+ }
52
+ // get x-forwarded-host
53
+ const originalHost = getOriginalHost(request.headers);
54
+ // get liveHost
55
+ const { host } = state.config;
56
+
57
+ if (originalHost !== host) {
58
+ // these have to match
59
+ state.log.debug(`x-forwarded-host: ${originalHost} does not match prod host: ${host}`);
60
+ return;
61
+ }
62
+ // get domainkey from config
63
+ const { domainkey } = state.config;
64
+ // get slack channel from config
65
+ const { slack } = state.config;
66
+ let hash;
67
+ if (typeof domainkey === 'string' || Array.isArray(domainkey)) {
68
+ hash = hashMe(originalHost, domainkey);
69
+ } else if (typeof slack === 'string' || Array.isArray(slack)) {
70
+ hash = hashMe(originalHost, slack);
71
+ }
72
+
73
+ if (hash) {
74
+ response.headers.set('x-rum-challenge', hash);
75
+ }
76
+ }
19
77
  /**
20
78
  * Handles options requests
21
79
  * @param {PipelineState} state pipeline options
@@ -46,6 +104,7 @@ export async function optionsPipe(state, request) {
46
104
  });
47
105
  await fetchConfigAll(state, request, res);
48
106
  await setCustomResponseHeaders(state, request, res);
107
+ setDomainkeyHeader(state, request, res);
49
108
 
50
109
  return res;
51
110
  } catch (e) {
@@ -37,10 +37,13 @@ export default async function fetchContent(state, req, res) {
37
37
  const ret = await state.s3Loader.getObject(bucketId, key);
38
38
 
39
39
  // check for redirect
40
- const redirectLocation = ret.headers.get('x-amz-meta-redirect-location');
40
+ let redirectLocation = ret.headers.get('x-amz-meta-redirect-location');
41
41
  if (redirectLocation) {
42
42
  res.status = 301;
43
43
  res.body = '';
44
+ if (redirectLocation.startsWith('/') && state.info.selector === 'plain') {
45
+ redirectLocation += '.plain.html';
46
+ }
44
47
  res.headers.set('location', redirectLocation);
45
48
  res.headers.set('x-surrogate-key', await computeSurrogateKey(`${contentBusId}${info.path}`));
46
49
  res.error = 'moved';