@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 +7 -0
- package/package.json +3 -3
- package/src/forms-pipe.js +14 -6
- package/src/steps/rewrite-icons.js +6 -2
- package/src/steps/validate-captcha.js +66 -0
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.
|
|
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
|
|
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.
|
|
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 =
|
|
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
|
+
}
|