@adobe/helix-html-pipeline 4.0.0 → 4.0.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,10 @@
1
+ ## [4.0.1](https://github.com/adobe/helix-html-pipeline/compare/v4.0.0...v4.0.1) (2023-07-18)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * ignore urns, hrefs, code blocks for rewrite-icons ([d61e23a](https://github.com/adobe/helix-html-pipeline/commit/d61e23a6a64ea764b935dd6a4d396c409d00bcb9)), closes [#348](https://github.com/adobe/helix-html-pipeline/issues/348)
7
+
1
8
  # [4.0.0](https://github.com/adobe/helix-html-pipeline/compare/v3.11.20...v4.0.0) (2023-07-11)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-html-pipeline",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "Helix HTML Pipeline",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -39,7 +39,7 @@
39
39
  "node": ">=16.x"
40
40
  },
41
41
  "dependencies": {
42
- "@adobe/helix-markdown-support": "6.1.3",
42
+ "@adobe/helix-markdown-support": "6.2.1",
43
43
  "@adobe/helix-shared-utils": "3.0.0",
44
44
  "@adobe/mdast-util-gridtables": "2.0.1",
45
45
  "@adobe/remark-gridtables": "1.0.4",
@@ -73,7 +73,7 @@
73
73
  "@semantic-release/git": "10.0.1",
74
74
  "@semantic-release/npm": "10.0.4",
75
75
  "c8": "8.0.0",
76
- "eslint": "8.44.0",
76
+ "eslint": "8.45.0",
77
77
  "eslint-import-resolver-exports": "1.0.0-beta.5",
78
78
  "eslint-plugin-header": "3.1.1",
79
79
  "eslint-plugin-import": "2.27.5",
package/src/forms-pipe.js CHANGED
@@ -15,6 +15,7 @@ import fetchConfigAll from './steps/fetch-config-all.js';
15
15
  import setCustomResponseHeaders from './steps/set-custom-response-headers.js';
16
16
  import { authenticate } from './steps/authenticate.js';
17
17
  import fetchConfig from './steps/fetch-config.js';
18
+ import validateCaptcha from './steps/validate-captcha.js';
18
19
 
19
20
  function error(log, msg, status, response) {
20
21
  log.error(msg);
@@ -126,12 +127,6 @@ export async function formsPipe(state, req) {
126
127
  return error(log, 'POST to URL with extension not allowed', 405, res);
127
128
  }
128
129
 
129
- // head workbook in content bus
130
- const resourceFetchResponse = await s3Loader.headObject('helix-content-bus', `${contentBusId}/${partition}${resourcePath}`);
131
- if (resourceFetchResponse.status !== 200) {
132
- return resourceFetchResponse;
133
- }
134
-
135
130
  let body;
136
131
  try {
137
132
  body = await extractBodyData(req);
@@ -139,6 +134,19 @@ export async function formsPipe(state, req) {
139
134
  return error(log, err.message, 400, res);
140
135
  }
141
136
 
137
+ // verify captcha if needed
138
+ try {
139
+ await validateCaptcha(state, body);
140
+ } catch (e) {
141
+ return error(log, e.message, e.code, res);
142
+ }
143
+
144
+ // head workbook in content bus
145
+ const resourceFetchResponse = await s3Loader.headObject('helix-content-bus', `${contentBusId}/${partition}${resourcePath}`);
146
+ if (resourceFetchResponse.status !== 200) {
147
+ return resourceFetchResponse;
148
+ }
149
+
142
150
  const sheets = resourceFetchResponse.headers.get('x-amz-meta-x-sheet-names');
143
151
  if (!sheets) {
144
152
  return error(log, `Target workbook at ${resourcePath} missing x-sheet-names header.`, 403, res);
@@ -11,9 +11,9 @@
11
11
  */
12
12
  /* eslint-disable no-param-reassign */
13
13
  import { h } from 'hastscript';
14
- import { CONTINUE, visit } from 'unist-util-visit';
14
+ import { CONTINUE, SKIP, visit } from 'unist-util-visit';
15
15
 
16
- const REGEXP_ICON = /:(#?[a-z_-]+[a-z\d]*):/gi;
16
+ const REGEXP_ICON = /(?<!(?:https?|urn)[^\s]*):(#?[a-z_-]+[a-z\d]*):/gi;
17
17
 
18
18
  /**
19
19
  * Create a <span> icon element:
@@ -44,9 +44,13 @@ function createIcon(value) {
44
44
  export default function rewrite({ content }) {
45
45
  const { hast } = content;
46
46
  visit(hast, (node, idx, parent) => {
47
+ if (node.tagName === 'code') {
48
+ return SKIP;
49
+ }
47
50
  if (node.type !== 'text') {
48
51
  return CONTINUE;
49
52
  }
53
+
50
54
  const text = node.value;
51
55
  let lastIdx = 0;
52
56
  for (const match of text.matchAll(REGEXP_ICON)) {
@@ -0,0 +1,66 @@
1
+ /*
2
+ * Copyright 2023 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import { PipelineStatusError } from '../PipelineStatusError.js';
14
+
15
+ async function validateGoogleCaptchaV2(body, fetch, secretKey) {
16
+ const captchaToken = body.data?.['g-recaptcha-response']
17
+ || body.data?.find((x) => (x.name === 'g-recaptcha-response'))?.value;
18
+
19
+ if (!captchaToken) {
20
+ throw new PipelineStatusError(400, 'Captcha token missing from request body');
21
+ }
22
+
23
+ const response = await fetch('https://www.google.com/recaptcha/api/siteverify', {
24
+ method: 'POST',
25
+ body: new URLSearchParams({ secret: secretKey, response: captchaToken }),
26
+ });
27
+ if (!response.ok) {
28
+ throw new PipelineStatusError(500, `Captcha validation request returned ${response.status}`);
29
+ }
30
+ const responseData = await response.json();
31
+ if (!responseData.success) {
32
+ throw new PipelineStatusError(400, 'Captcha validation failed');
33
+ }
34
+ }
35
+
36
+ const SUPPORTED_CAPTCHA_TYPES = {
37
+ 'reCaptcha v2': validateGoogleCaptchaV2,
38
+ };
39
+
40
+ /**
41
+ *
42
+ * @param {PipelineState} state Pipeline options
43
+ * @param {Object} body Request body
44
+ * @returns {void}
45
+ */
46
+ export default async function validateCaptcha(state, body) {
47
+ const { fetch, config } = state;
48
+ const { captcha } = config;
49
+
50
+ // If captcha type is not configured, do nothing
51
+ if (!captcha?.type) {
52
+ return;
53
+ }
54
+
55
+ // Check captcha type is correctly configured
56
+ const validator = SUPPORTED_CAPTCHA_TYPES[captcha.type];
57
+ if (!validator) {
58
+ throw new PipelineStatusError(500, `The captcha type ${captcha.type} is not supported.`);
59
+ }
60
+ if (!captcha.secret) {
61
+ throw new PipelineStatusError(500, 'Captcha secret key is not configured.');
62
+ }
63
+
64
+ // Perform validation
65
+ await validator(body, fetch, captcha.secret);
66
+ }