diff --git a/content/webhooks/using-webhooks/editing-webhooks.md b/content/webhooks/using-webhooks/editing-webhooks.md new file mode 100644 index 000000000000..6119f4d03e81 --- /dev/null +++ b/content/webhooks/using-webhooks/editing-webhooks.md @@ -0,0 +1,71 @@ +--- +title: Editing webhooks +intro: 'After creating a webhook, you can make changes to it.' +versions: + fpt: '*' + ghes: '*' + ghae: '*' + ghec: '*' +topics: + - Webhooks +--- + +## About editing webhooks + +You can edit a webhook to change any of the settings that were selected when the webhook was initially created. For more information about the settings you can select while creating a webhook, see "[AUTOTITLE](/webhooks/using-webhooks/creating-webhooks)." + +## Editing a repository webhook + +You can edit a webhook that was created in a specific repository. You must be a repository owner or have admin access in the repository to edit webhooks in that repository. + +You can use the {% data variables.product.prodname_dotcom %} web interface or the REST API to edit a repository webhook. For more information about using the REST API to edit a repository webhook, see "[AUTOTITLE](/rest/webhooks/repos#update-a-repository-webhook)." + +{% data reusables.repositories.navigate-to-repo %} +{% data reusables.repositories.sidebar-settings %} +{% data reusables.webhooks.sidebar_webhooks %} +{% data reusables.webhooks.edit_webhook %} +{% data reusables.webhooks.edit_webhook_make_changes %} +{% data reusables.webhooks.update_webhook %} + +## Editing an organization webhook + +You can edit a webhook that was created in a specific organization. You must be an organization owner to edit webhooks in that organization. + +You can use the {% data variables.product.prodname_dotcom %} web interface or the REST API to edit an organization webhook. For more information about using the REST API to create an organization webhook, see "[AUTOTITLE](/rest/orgs/webhooks#update-an-organization-webhook)." + +{% data reusables.repositories.navigate-to-repo %} +{% data reusables.repositories.sidebar-settings %} +{% data reusables.webhooks.sidebar_webhooks %} +{% data reusables.webhooks.edit_webhook %} +{% data reusables.webhooks.edit_webhook_make_changes %} +{% data reusables.webhooks.update_webhook %} + +{% ifversion ghes or ghae or ghec %} + +## Editing a global webhook for a {% data variables.product.prodname_enterprise %} + +Enterprise owners can edit a global webhook to change any of the settings that were selected when the webhook was initially created. For more information, see "[AUTOTITLE](/admin/monitoring-activity-in-your-enterprise/exploring-user-activity-in-your-enterprise/managing-global-webhooks#editing-a-global-webhook)." + +{% endif %} + +{% ifversion fpt or ghec %} + +## Editing a {% data variables.product.prodname_marketplace %} webhook + +You can edit a webhook that was created for an app that you published in {% data variables.product.prodname_marketplace %}. Only the owner of the app, or an app manager for the organization that owns the app, can edit a {% data variables.product.prodname_marketplace %} webhook. For more information, see "[AUTOTITLE](/apps/publishing-apps-to-github-marketplace/using-the-github-marketplace-api-in-your-app/webhook-events-for-the-github-marketplace-api)." + +1. Navigate to your [{% data variables.product.prodname_marketplace %} listing page](https://github.com/marketplace/manage). +1. Next to the {% data variables.product.prodname_marketplace %} listing that you want to view webhook deliveries for, click **Manage listing**. +1. In the sidebar, click **Webhook**. +{% data reusables.webhooks.edit_webhook_make_changes %} +{% data reusables.webhooks.update_webhook %} + +## Editing a {% data variables.product.prodname_sponsors %} webhook + +You can edit a webhook that was created for a {% data variables.product.prodname_sponsors %} account. Only the owner of the sponsored account can edit sponsorship webhooks for that account. For more information, see "[AUTOTITLE](/sponsors/integrating-with-github-sponsors/configuring-webhooks-for-events-in-your-sponsored-account)." + +{% endif %} + +## Editing webhooks for a {% data variables.product.prodname_github_app %} + +You can activate or deactivate a {% data variables.product.prodname_github_app %} webhook, change the webhook events that a {% data variables.product.prodname_github_app %} subscribes to, or make changes to other basic settings of a {% data variables.product.prodname_github_app %}'s webhook. For more information, see "[AUTOTITLE](/apps/maintaining-github-apps/modifying-a-github-app-registration)." diff --git a/content/webhooks/using-webhooks/index.md b/content/webhooks/using-webhooks/index.md index c8b19cb470cc..71e9c6d940ee 100644 --- a/content/webhooks/using-webhooks/index.md +++ b/content/webhooks/using-webhooks/index.md @@ -11,8 +11,10 @@ topics: children: - /creating-webhooks - /handling-webhook-deliveries - - /securing-your-webhooks + - /validating-webhook-deliveries + - /editing-webhooks - /handling-failed-webhook-deliveries - /disabling-webhooks - /best-practices-for-using-webhooks --- + diff --git a/content/webhooks/using-webhooks/securing-your-webhooks.md b/content/webhooks/using-webhooks/securing-your-webhooks.md deleted file mode 100644 index 9dbde2960879..000000000000 --- a/content/webhooks/using-webhooks/securing-your-webhooks.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -title: Securing your webhooks -intro: 'Ensure your server is only receiving the expected {% data variables.product.prodname_dotcom %} requests for security reasons.' -redirect_from: - - /webhooks/securing - - /developers/webhooks-and-events/securing-your-webhooks - - /developers/webhooks-and-events/webhooks/securing-your-webhooks - - /webhooks-and-events/webhooks/securing-your-webhooks - - /webhooks/webhooks/securing-your-webhooks - - /webhooks/securing-your-webhooks -versions: - fpt: '*' - ghes: '*' - ghae: '*' - ghec: '*' -topics: - - Webhooks ---- -Once your server is configured to receive payloads, it'll listen for any payload sent to the endpoint you configured. For security reasons, you probably want to limit requests to those coming from GitHub. There are a few ways to go about this (for example, you could opt to allow requests from GitHub's IP address) but a far easier method is to set up a secret token and validate the information. - -{% data reusables.webhooks.webhooks-rest-api-links %} - -## Setting your secret token - -You'll need to set up your secret token in two places: GitHub and your server. - -To set your token on GitHub: - -1. Navigate to the repository where you're setting up your webhook. -{% data reusables.repositories.sidebar-settings %} -1. In the left sidebar, click **{% octicon "webhook" aria-hidden="true" %} Webhooks**. -1. Next to the webhook, click **Edit**. -1. In the "Secret" field, type a random string with high entropy. You can generate a string with `ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'` in the terminal, for example. -1. Click **Update Webhook**. - -Next, set up an environment variable on your server that stores this token. Typically, this is as simple as running: - -```shell -export SECRET_TOKEN=YOUR-TOKEN -``` - -**Never** hardcode the token into your app! - -## Validating payloads from GitHub - -When your secret token is set, {% data variables.product.product_name %} uses it to create a hash signature with each payload. This hash signature is included with the headers of each request as `x-hub-signature-256`. - -{% ifversion fpt or ghes or ghec %} -{% note %} - -**Note:** For backward-compatibility, we also include the `x-hub-signature` header that is generated using the SHA-1 hash function. If possible, we recommend that you use the `x-hub-signature-256` header for improved security. The examples below demonstrate using the `x-hub-signature-256` header. - -{% endnote %} -{% endif %} - -You should calculate a hash using your `SECRET_TOKEN`, and ensure that the result matches the hash from {% data variables.product.product_name %}. {% data variables.product.product_name %} uses an HMAC hex digest to compute the hash. - -{% note %} - -**Note:** Webhook payloads can contain unicode characters. If your language and server implementation specifies a character encoding, ensure that you handle the payload as UTF-8. - -{% endnote %} - -Your language and server implementations may differ from the following examples. However, there are a number of very important things to point out: - -- No matter which implementation you use, the hash signature starts with `sha256=`, using the key of your secret token and your payload body. - -- Using a plain `==` operator is **not advised**. A method like [`secure_compare`][secure_compare] or [`crypto.timingSafeEqual`][timingSafeEqual] performs a "constant time" string comparison, which helps mitigate certain timing attacks against regular equality operators, or regular loops in JIT-optimized languages. - -### Test values - -Regardless of the programming language that you use to implement HMAC verification in your code, you can use the following `secret` and `payload` values to verify that your implementation is correct. - -- secret: "It's a Secret to Everybody" -- payload: "Hello, World!" - -If your implementation is correct and uses the SHA-256 algorithm, the signatures that you generate should match the following signature values: - -- signature: 757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17 -- x-hub-signature: sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17 - -If your implementation is correct and uses the SHA-1 algorithm, the signatures that you generate should match the following signature values: - -- signature: 01dc10d0c83e72ed246219cdd91669667fe2ca59 -- x-hub-signature: sha1=01dc10d0c83e72ed246219cdd91669667fe2ca59 - -### Ruby example - -For example, you can define the following `verify_signature` function: - -``` ruby -def verify_signature(payload_body) - signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload_body) - return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE_256']) -end -``` - -Then you can call it when you receive a webhook payload: - -``` ruby -post '/payload' do - request.body.rewind - payload_body = request.body.read - verify_signature(payload_body) - push = JSON.parse(payload_body) - "I got some JSON: #{push.inspect}" -end -``` - -### Python example - -For example, you can define the following `verify_signature` function and call it when you receive a webhook payload: - -```python -import hashlib -import hmac -def verify_signature(payload_body, secret_token, signature_header): - """Verify that the payload was sent from GitHub by validating SHA256. - - Raise and return 403 if not authorized. - - Args: - payload_body: original request body to verify (request.body()) - secret_token: GitHub app webhook token (WEBHOOK_SECRET) - signature_header: header received from GitHub (x-hub-signature-256) - """ - if not signature_header: - raise HTTPException(status_code=403, detail="x-hub-signature-256 header is missing!") - hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256) - expected_signature = "sha256=" + hash_object.hexdigest() - if not hmac.compare_digest(expected_signature, signature_header): - raise HTTPException(status_code=403, detail="Request signatures didn't match!") -``` - -### JavaScript example - -For example, you can define the following `verifySignature` function and call it in any JavaScript environment when you receive a webhook payload: - -```javascript -let encoder = new TextEncoder(); - -async function verifySignature(secret, header, payload) { - let parts = header.split("="); - let sigHex = parts[1]; - - let algorithm = { name: "HMAC", hash: { name: 'SHA-256' } }; - - let keyBytes = encoder.encode(secret); - let extractable = false; - let key = await crypto.subtle.importKey( - "raw", - keyBytes, - algorithm, - extractable, - [ "sign", "verify" ], - ); - - let sigBytes = hexToBytes(sigHex); - let dataBytes = encoder.encode(payload); - let equal = await crypto.subtle.verify( - algorithm.name, - key, - sigBytes, - dataBytes, - ); - - return equal; -} - -function hexToBytes(hex) { - let len = hex.length / 2; - let bytes = new Uint8Array(len); - - let index = 0; - for (let i = 0; i < hex.length; i += 2) { - let c = hex.slice(i, i + 2); - let b = parseInt(c, 16); - bytes[index] = b; - index += 1; - } - - return bytes; -} -``` - -### Typescript example - -For example, you can define the following `verify_signature` function and call it when you receive a webhook payload: - -```javascript copy -import * as crypto from "crypto"; - -const WEBHOOK_SECRET: string = process.env.WEBHOOK_SECRET; - -const verify_signature = (req: Request) => { - const signature = crypto - .createHmac("sha256", WEBHOOK_SECRET) - .update(JSON.stringify(req.body)) - .digest("hex"); - let trusted = Buffer.from(`sha256=${signature}`, 'ascii'); - let untrusted = Buffer.from(req.headers.get("x-hub-signature-256"), 'ascii'); - return crypto.timingSafeEqual(trusted, untrusted); -}; - -const handleWebhook = (req: Request, res: Response) => { - if (!verify_signature(req)) { - res.status(401).send("Unauthorized"); - return; - } - // The rest of your logic here -}; -``` - -[secure_compare]: https://www.rubydoc.info/gems/rack/Rack%2FUtils:secure_compare -[timingSafeEqual]: https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b diff --git a/content/webhooks/using-webhooks/validating-webhook-deliveries.md b/content/webhooks/using-webhooks/validating-webhook-deliveries.md new file mode 100644 index 000000000000..24fb49b995e7 --- /dev/null +++ b/content/webhooks/using-webhooks/validating-webhook-deliveries.md @@ -0,0 +1,205 @@ +--- +title: Validating webhook deliveries +intro: 'You can use a webhook secret to verify that a webhook delivery is from {% data variables.product.company_short %}.' +redirect_from: + - /webhooks/securing + - /developers/webhooks-and-events/securing-your-webhooks + - /developers/webhooks-and-events/webhooks/securing-your-webhooks + - /webhooks-and-events/webhooks/securing-your-webhooks + - /webhooks/webhooks/securing-your-webhooks + - /webhooks/securing-your-webhooks + - /webhooks/using-webhooks/securing-your-webhooks +versions: + fpt: '*' + ghes: '*' + ghae: '*' + ghec: '*' +topics: + - Webhooks +--- + +## About webhook deliveries + +Once your server is configured to receive payloads, it will listen for any delivery that's sent to the endpoint you configured. For security reasons, you should only process deliveries from {% data variables.product.prodname_dotcom %}. + +To ensure your server only processes deliveries from {% data variables.product.prodname_dotcom %}, you need to: + +1. Create a secret token for a webhook. +1. Store the token securely on your server. +1. Validate incoming webhook payloads against the token, to verify they are coming from {% data variables.product.prodname_dotcom %}. + +## Creating a secret token + +You can create a new webhook with a secret token, or you can add a secret token to an existing webhook. When creating a secret token, you should choose a random string of text with high entropy. + +- _To create a new webhook with a secret token_, see "[AUTOTITLE](/webhooks/using-webhooks/creating-webhooks)." +- _To add a secret token to an existing webhook_, edit the webhook's settings. Under "Secret", type a string to use as a `secret` key. For more information, see "[AUTOTITLE](/webhooks/using-webhooks/editing-webhooks)." + +## Securely storing the secret token + +After creating a secret token, you should store it in a secure location that your server can access. Never hardcode a token into an application or push a token to any repository. For more information about how to use authentication credentials securely in your code, see "[AUTOTITLE](/rest/overview/keeping-your-api-credentials-secure#use-authentication-credentials-securely-in-your-code)." + +## Validating webhook deliveries + +{% data variables.product.product_name %} will use your secret token to create a hash signature that's sent to you with each payload. The hash signature will appear in each delivery as the value of the `X-Hub-Signature-256` header. For more information, see "[AUTOTITLE](/webhooks/webhook-events-and-payloads#delivery-headers)." + +In your code that handles webhook deliveries, you should calculate a hash using your secret token. Then, compare the hash that {% data variables.product.company_short %} sent with the expected hash that you calculated, and ensure that they match. For examples showing how to validate the hashes in various programming languages, see "[Examples](#examples)." + +There are a few important things to keep in mind when validating webhook payloads: + +- {% data variables.product.product_name %} uses an HMAC hex digest to compute the hash. +- The hash signature always starts with `sha256=`. +- The hash signature is generated using your webhook's secret token and the payload contents. +- If your language and server implementation specifies a character encoding, ensure that you handle the payload as UTF-8. Webhook payloads can contain unicode characters. +- Never use a plain `==` operator. Instead consider using a method like [`secure_compare`][secure_compare] or [`crypto.timingSafeEqual`][timingSafeEqual], which performs a "constant time" string comparison to help mitigate certain timing attacks against regular equality operators, or regular loops in JIT-optimized languages. + +### Testing the webhook payload validation + +You can use the following `secret` and `payload` values to verify that your implementation is correct: + +- `secret`: "It's a Secret to Everybody" +- `payload`: "Hello, World!" + +If your implementation is correct, the signatures that you generate should match the following signature values: + +- signature: `757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17` +- X-Hub-Signature-256: `sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17` + +### Examples + +You can use your programming language of choice to implement HMAC verification in your code. Following are some examples showing how an implementation might look in various programming languages. + +#### Ruby example + +For example, you can define the following `verify_signature` function: + +``` ruby +def verify_signature(payload_body) + signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload_body) + return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE_256']) +end +``` + +Then you can call it when you receive a webhook payload: + +``` ruby +post '/payload' do + request.body.rewind + payload_body = request.body.read + verify_signature(payload_body) + push = JSON.parse(payload_body) + "I got some JSON: #{push.inspect}" +end +``` + +#### Python example + +For example, you can define the following `verify_signature` function and call it when you receive a webhook payload: + +```python +import hashlib +import hmac +def verify_signature(payload_body, secret_token, signature_header): + """Verify that the payload was sent from GitHub by validating SHA256. + + Raise and return 403 if not authorized. + + Args: + payload_body: original request body to verify (request.body()) + secret_token: GitHub app webhook token (WEBHOOK_SECRET) + signature_header: header received from GitHub (x-hub-signature-256) + """ + if not signature_header: + raise HTTPException(status_code=403, detail="x-hub-signature-256 header is missing!") + hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256) + expected_signature = "sha256=" + hash_object.hexdigest() + if not hmac.compare_digest(expected_signature, signature_header): + raise HTTPException(status_code=403, detail="Request signatures didn't match!") +``` + +#### JavaScript example + +For example, you can define the following `verifySignature` function and call it in any JavaScript environment when you receive a webhook payload: + +```javascript +let encoder = new TextEncoder(); + +async function verifySignature(secret, header, payload) { + let parts = header.split("="); + let sigHex = parts[1]; + + let algorithm = { name: "HMAC", hash: { name: 'SHA-256' } }; + + let keyBytes = encoder.encode(secret); + let extractable = false; + let key = await crypto.subtle.importKey( + "raw", + keyBytes, + algorithm, + extractable, + [ "sign", "verify" ], + ); + + let sigBytes = hexToBytes(sigHex); + let dataBytes = encoder.encode(payload); + let equal = await crypto.subtle.verify( + algorithm.name, + key, + sigBytes, + dataBytes, + ); + + return equal; +} + +function hexToBytes(hex) { + let len = hex.length / 2; + let bytes = new Uint8Array(len); + + let index = 0; + for (let i = 0; i < hex.length; i += 2) { + let c = hex.slice(i, i + 2); + let b = parseInt(c, 16); + bytes[index] = b; + index += 1; + } + + return bytes; +} +``` + +#### Typescript example + +For example, you can define the following `verify_signature` function and call it when you receive a webhook payload: + +```javascript copy +import * as crypto from "crypto"; + +const WEBHOOK_SECRET: string = process.env.WEBHOOK_SECRET; + +const verify_signature = (req: Request) => { + const signature = crypto + .createHmac("sha256", WEBHOOK_SECRET) + .update(JSON.stringify(req.body)) + .digest("hex"); + let trusted = Buffer.from(`sha256=${signature}`, 'ascii'); + let untrusted = Buffer.from(req.headers.get("x-hub-signature-256"), 'ascii'); + return crypto.timingSafeEqual(trusted, untrusted); +}; + +const handleWebhook = (req: Request, res: Response) => { + if (!verify_signature(req)) { + res.status(401).send("Unauthorized"); + return; + } + // The rest of your logic here +}; +``` + +[secure_compare]: https://www.rubydoc.info/gems/rack/Rack%2FUtils:secure_compare +[timingSafeEqual]: https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b + +## Further reading + +- "[AUTOTITLE](/webhooks/using-webhooks/handling-webhook-deliveries)" +- "[AUTOTITLE](/webhooks/using-webhooks/best-practices-for-using-webhooks)" diff --git a/content/webhooks/webhook-events-and-payloads.md b/content/webhooks/webhook-events-and-payloads.md index c5367932d628..58b9d83620a5 100644 --- a/content/webhooks/webhook-events-and-payloads.md +++ b/content/webhooks/webhook-events-and-payloads.md @@ -38,9 +38,9 @@ HTTP POST payloads that are delivered to your webhook's configured URL endpoint - `X-GitHub-Event`: The name of the event that triggered the delivery. - `X-GitHub-Delivery`: A globally unique identifier (GUID) to identify the delivery.{% ifversion ghes or ghae %} - `X-GitHub-Enterprise-Version`: The version of the {% data variables.product.prodname_ghe_server %} instance that sent the HTTP POST payload. -- `X-GitHub-Enterprise-Host`: The hostname of the {% data variables.product.prodname_ghe_server %} instance that sent the HTTP POST payload.{% endif %} -- `X-Hub-Signature-256`: This header is sent if the webhook is configured with a secret. This is the HMAC hex digest of the request body, and is generated using the SHA-256 hash function and the configured secret as the HMAC `key`. For more information, see "[AUTOTITLE](/webhooks/using-webhooks/securing-your-webhooks)."{% ifversion not ghae %} -- `X-Hub-Signature`: This header is sent if the webhook is configured with a secret. This is the HMAC hex digest of the request body, and is generated using the SHA-1 hash function and the configured secret as the HMAC `key`. `X-Hub-Signature` is provided for compatibility with existing integrations, and we recommend that you use the more secure `X-Hub-Signature-256` instead.{% endif %} +- `X-GitHub-Enterprise-Host`: The hostname of the {% data variables.product.prodname_ghe_server %} instance that sent the HTTP POST payload.{% endif %}{% ifversion not ghae %} +- `X-Hub-Signature`: This header is sent if the webhook is configured with a `secret`. This is the HMAC hex digest of the request body, and is generated using the SHA-1 hash function and the `secret` as the HMAC `key`. `X-Hub-Signature` is provided for compatibility with existing integrations. We recommend that you use the more secure `X-Hub-Signature-256` instead.{% endif %} +- `X-Hub-Signature-256`: This header is sent if the webhook is configured with a `secret`. This is the HMAC hex digest of the request body, and is generated using the SHA-256 hash function and the `secret` as the HMAC `key`. For more information, see "[AUTOTITLE](/webhooks/using-webhooks/securing-your-webhooks)." - `User-Agent`: This header will always have the prefix `GitHub-Hookshot/`. - `X-GitHub-Hook-Installation-Target-Type`: The type of resource where the webhook was created. - `X-GitHub-Hook-Installation-Target-ID`: The unique identifier of the resource where the webhook was created. diff --git a/data/reusables/webhooks/edit_webhook.md b/data/reusables/webhooks/edit_webhook.md new file mode 100644 index 000000000000..db0cff3638b6 --- /dev/null +++ b/data/reusables/webhooks/edit_webhook.md @@ -0,0 +1 @@ +1. Next to the webhook you'd like to edit, click **Edit**. diff --git a/data/reusables/webhooks/edit_webhook_make_changes.md b/data/reusables/webhooks/edit_webhook_make_changes.md new file mode 100644 index 000000000000..76b48a3a6698 --- /dev/null +++ b/data/reusables/webhooks/edit_webhook_make_changes.md @@ -0,0 +1 @@ +1. Make any desired changes to the webhook settings. diff --git a/data/reusables/webhooks/update_webhook.md b/data/reusables/webhooks/update_webhook.md new file mode 100644 index 000000000000..72dd921255bb --- /dev/null +++ b/data/reusables/webhooks/update_webhook.md @@ -0,0 +1 @@ +1. Click **Update Webhook**. diff --git a/src/content-linter/tests/lint-files.js b/src/content-linter/tests/lint-files.js index 197a8d5ced14..8ec3ea361227 100755 --- a/src/content-linter/tests/lint-files.js +++ b/src/content-linter/tests/lint-files.js @@ -345,7 +345,6 @@ describe('lint markdown content', () => { let content, ast, links, - yamlScheduledWorkflows, isHidden, isEarlyAccess, isSitePolicy, @@ -377,40 +376,6 @@ describe('lint markdown content', () => { visit(ast, ['link', 'definition'], (node) => { links.push(node.url) }) - - yamlScheduledWorkflows = [] - visit(ast, 'code', (node) => { - if ( - /ya?ml/.test(node.lang) && - node.value.includes('schedule:') && - node.value.includes('cron:') - ) { - yamlScheduledWorkflows.push(node.value) - } - }) - - const context = { - currentLanguage: 'en', - // Any Liquid that might use our `ifversion` plugin requires and - // expects that there's a `currentVersionObj` object present in the - // environment. - currentVersionObj: {}, - } - - // visit is not async-friendly so we need to do an async map to parse the YML snippets - yamlScheduledWorkflows = ( - await Promise.all( - yamlScheduledWorkflows.map(async (snippet) => { - // If we don't parse the Liquid first, yaml loading chokes on {% raw %} tags - const rendered = await liquid.parseAndRender(snippet, context) - const parsed = yaml.load(rendered) - if (!parsed?.on?.schedule?.cron) return [] - return parsed.on.schedule - }), - ) - ) - .flat() - .map((schedule) => schedule.cron) }) test('placeholder string is not present in any markdown files', async () => { @@ -481,20 +446,6 @@ describe('lint markdown content', () => { expect(matches.length, errorMessage).toBe(0) }) - test('yaml snippets that include scheduled workflows must not run on the hour', async () => { - const hourlySchedules = yamlScheduledWorkflows.filter((schedule) => { - const hour = schedule.split(' ')[0] - // return any minute cron segments that equal 0, 00, 000, etc. - return !/[^0]/.test(hour) - }) - expect(hourlySchedules).toEqual([]) - }) - - // Note this only ensures that scheduled workflow snippets are unique _per Markdown file_ - test('yaml snippets that include scheduled workflows run at unique times', () => { - expect(yamlScheduledWorkflows.length).toEqual(new Set(yamlScheduledWorkflows).size) - }) - test('must not leak Early Access doc URLs', async () => { // Only execute for docs that are NOT Early Access if (!isEarlyAccess) {