Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merl 1749 faust should warn if the secret key is invalid #1777

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7ad4d25
Added validation check for disparate FAUST_SECRET_KEYs
TeresaGobble Jan 30, 2024
9ec13b9
typo
TeresaGobble Jan 30, 2024
ab66bcb
Continued tinkering with /validate_secret_key endpoint
TeresaGobble Feb 5, 2024
900d903
Cleaned up testing logs
TeresaGobble Feb 6, 2024
eee73ec
Tweaked new endpoint
TeresaGobble Feb 6, 2024
5ed7048
Updated package-lock
TeresaGobble Feb 6, 2024
28dc657
Added @types/node as part of linting validateFaustEnvVars
TeresaGobble Feb 6, 2024
d74940b
Ran PHP Code Sniffer command on file to fix linting errors
TeresaGobble Feb 6, 2024
8f6d779
Revert "typo"
TeresaGobble Feb 7, 2024
08dd232
Revert "Ran PHP Code Sniffer command on file to fix linting errors"
TeresaGobble Feb 7, 2024
f9328a7
phpcs:fix run using composer after adding ignore command to phpcs.xml…
TeresaGobble Feb 7, 2024
03fb8fb
Fixed inline phpcs error
TeresaGobble Feb 7, 2024
50084f9
Removed error_log() and corresponding comment
TeresaGobble Feb 7, 2024
456150d
Updated experimental-nextjs-app-support to "^0.7.0"
TeresaGobble Feb 7, 2024
426a93e
Revert "Updated experimental-nextjs-app-support to "^0.7.0""
TeresaGobble Feb 7, 2024
d6bff3c
Added await to fix linting issue
TeresaGobble Feb 7, 2024
9120182
Awaited function validateFaustEnvVars in test
TeresaGobble Feb 9, 2024
91c7211
Update packages/faustwp-cli/src/healthCheck/validateFaustEnvVars.ts
TeresaGobble Feb 9, 2024
7caab39
Edited if conditional for secret key validation according to Blake fe…
TeresaGobble Feb 9, 2024
0bcc957
Merge branch 'MERL-1749-Faust-should-warn-if-the-secret-key-is-invali…
TeresaGobble Feb 9, 2024
3b93522
refactored exit(1) to remove import statement
TeresaGobble Feb 9, 2024
b216001
Update plugins/faustwp/includes/rest/callbacks.php
TeresaGobble Feb 9, 2024
ef3ebbd
Removed devDependency @types/node per Blake feedback
TeresaGobble Feb 9, 2024
a6785fc
Added unit test to ensure error is logged when status returns 401
TeresaGobble Feb 9, 2024
b5363fd
Edited snapshot
TeresaGobble Feb 9, 2024
4923329
Add snapshot
TeresaGobble Feb 9, 2024
1450b5a
Tweaked .toMatchSnapshot content
TeresaGobble Feb 9, 2024
30f088e
Update packages/faustwp-cli/src/healthCheck/validateFaustEnvVars.ts
TeresaGobble Feb 13, 2024
0ea3d5f
Removed types/node from package-lock.json
TeresaGobble Feb 13, 2024
bb16211
Merge branch 'canary' into MERL-1749-Faust-should-warn-if-the-secret-…
TeresaGobble Feb 15, 2024
d5adc5b
Update packages/faustwp-cli/src/healthCheck/validateFaustEnvVars.ts
TeresaGobble Feb 15, 2024
3e70aea
Update packages/faustwp-cli/src/healthCheck/validateFaustEnvVars.ts
TeresaGobble Feb 15, 2024
e57516b
Refactored getWpSecret to const secretWp
TeresaGobble Feb 15, 2024
7def6f4
Removed the sprintf call
TeresaGobble Feb 15, 2024
67dd4b8
Fixed test and snap to match implementation changes
TeresaGobble Feb 15, 2024
91b7dc3
Create honest-buckets-cry.md
TeresaGobble Feb 15, 2024
f00297d
Update .changeset/honest-buckets-cry.md
TeresaGobble Feb 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/honest-buckets-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@faustwp/cli": patch
"@faustwp/wordpress-plugin": patch
---

Faust now warns you if the secret key in your environment is invalid or incorrect.
43 changes: 41 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/faustwp-cli/src/healthCheck/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { verifyGraphQLEndpoint } from './verifyGraphQLEndpoint.js';
* Ensure that everything Faust requires to run is available.
*/
export async function healthCheck(): Promise<void> {
// Check Faust Env varibles before continuing.
validateFaustEnvVars();
// Check Faust Env variables before continuing.
await validateFaustEnvVars();

// Perform our health checks.
await verifyGraphQLEndpoint();
Expand Down
36 changes: 29 additions & 7 deletions packages/faustwp-cli/src/healthCheck/validateFaustEnvVars.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getWpSecret } from '../utils/index.js';
import { getWpSecret, getWpUrl } from '../utils/index.js';
import { errorLog, infoLog, warnLog } from '../stdout/index.js';

export function isWPEngineComSubdomain(url: string) {
Expand All @@ -10,7 +10,9 @@ export function isWPEngineComSubdomain(url: string) {
/**
* Validates that the appropriate Faust related environment variables are set.
*/
export const validateFaustEnvVars = () => {
export const validateFaustEnvVars = async () => {
const secretWp = getWpSecret();

if (!process.env.NEXT_PUBLIC_WORDPRESS_URL) {
errorLog('Could not find NEXT_PUBLIC_WORDPRESS_URL environment variable.');

Expand All @@ -28,15 +30,12 @@ export const validateFaustEnvVars = () => {
);
}

if (!getWpSecret()) {
if (!secretWp) {
warnLog('Could not find FAUST_SECRET_KEY environment variable.');
warnLog('Some functionality may be limited.');
}

if (
process.env.NEXT_PUBLIC_WORDPRESS_URL.startsWith('http://') &&
getWpSecret()
) {
if (process.env.NEXT_PUBLIC_WORDPRESS_URL.startsWith('http://') && secretWp) {
warnLog('Your WordPress site is not running on https!');
warnLog(
'This is a security concern as all traffic with your secret key is in plain text.',
Expand All @@ -45,4 +44,27 @@ export const validateFaustEnvVars = () => {
'Please make sure your production Faust app runs with a WordPress instance on https!',
);
}

if (secretWp) {
// send secret key
const apiUrl = `${getWpUrl()}/?rest_route=/faustwp/v1/validate_secret_key`;
const headers = {
'x-faustwp-secret': secretWp,
};
try {
const response = await fetch(apiUrl, {
headers,
method: 'POST',
});
if (response.status === 401) {
// Unauthorized: User receives a 401 status code AND the message below
errorLog(
'Ensure your FAUST_SECRET_KEY environment variable matches your Secret Key in the Faust WordPress plugin settings',
mindctrl marked this conversation as resolved.
Show resolved Hide resolved
);
process.exit(1);
}
} catch (error) {
console.log('error', error);
}
TeresaGobble marked this conversation as resolved.
Show resolved Hide resolved
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`healthCheck/validateFaustEnvVars logs an error when the secret key validation fails: Ensure your FAUST_SECRET_KEY environment variable matches your Secret Key in the Faust WordPress plugin settings 1`] = `Promise {}`;
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
isWPEngineComSubdomain,
validateFaustEnvVars,
} from '../../src/healthCheck/validateFaustEnvVars';
import fetchMock from 'fetch-mock';

/**
* @jest-environment jsdom
*/
Expand All @@ -20,7 +22,7 @@ describe('healthCheck/validateFaustEnvVars', () => {
process.env = envBackup;
});

it('exits with a 1 exit code when the WordPress URL is undefined', () => {
it('exits with a 1 exit code when the WordPress URL is undefined', async () => {
// @ts-ignore
const mockExit = jest.spyOn(process, 'exit').mockImplementation((code) => {
if (code && code !== 0) {
Expand All @@ -30,15 +32,15 @@ describe('healthCheck/validateFaustEnvVars', () => {

// Use try/catch block to mock process.exit
try {
validateFaustEnvVars();
await validateFaustEnvVars();
} catch (err) {
console.log(err);
}

expect(mockExit).toHaveBeenCalledWith(1);
});

it('does not exit or throw an error when the WordPress URL is set', () => {
it('does not exit or throw an error when the WordPress URL is set', async () => {
// @ts-ignore
const mockExit = jest.spyOn(process, 'exit').mockImplementation((code) => {
if (code && code !== 0) {
Expand All @@ -48,10 +50,25 @@ describe('healthCheck/validateFaustEnvVars', () => {

process.env.NEXT_PUBLIC_WORDPRESS_URL = 'http://headless.local';

validateFaustEnvVars();
await validateFaustEnvVars();

expect(mockExit).toBeCalledTimes(0);
});

it('logs an error when the secret key validation fails', async () => {

process.env.NEXT_PUBLIC_WORDPRESS_URL = 'https://headless.local';
process.env.FAUST_SECRET_KEY = 'invalid-secret-key';

fetchMock.post('https://headless.local/wp-json/faustwp/v1/validate_secret_key', {
status: 401,
});

await validateFaustEnvVars();

return expect(Promise.resolve(validateFaustEnvVars())).toMatchSnapshot(`Ensure your FAUST_SECRET_KEY environment variable matches your Secret Key in the Faust WordPress plugin settings`);
});

});

describe('isWPEngineComTLD', () => {
Expand Down
47 changes: 47 additions & 0 deletions plugins/faustwp/includes/rest/callbacks.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ function register_rest_routes() {
)
);

register_rest_route(
'faustwp/v1',
'/validate_secret_key',
array(
'methods' => 'POST',
'callback' => __NAMESPACE__ . '\\handle_rest_validate_secret_key_callback',
'permission_callback' => __NAMESPACE__ . '\\rest_validate_secret_key_permission_callback',
)
);

/**
* Faust.js packages now use `faustwp/v1/authorize`.
*
Expand Down Expand Up @@ -333,6 +343,8 @@ function rest_process_telemetry_permission_callback( \WP_REST_Request $request )
return rest_authorize_permission_callback( $request );
}



/**
* Callback for WordPress register_rest_route() 'callback' parameter.
*
Expand Down Expand Up @@ -476,3 +488,38 @@ function handle_rest_telemetry_decision_callback( \WP_REST_Request $request ) {
);
return rest_ensure_response( $response );
}

/**
* Callback for WordPress register_rest_route() 'callback' parameter.
*
* Handle POST /faustwp/v1/validate_secret_key response.
*
* @link https://developer.wordpress.org/reference/functions/register_rest_route/
* @link https://developer.wordpress.org/rest-api/extending-the-rest-api/routes-and-endpoints/#endpoint-callback
*
* @param \WP_REST_Request $request Current \WP_REST_Request object.
*
* @return mixed A \WP_REST_Response, or \WP_Error.
*/
function handle_rest_validate_secret_key_callback( \WP_REST_Request $request ) {
return new \WP_REST_Response(
TeresaGobble marked this conversation as resolved.
Show resolved Hide resolved
esc_html__( 'Secret key validated!', 'faustwp' ),
200
);
}

/**
* Callback to check permissions for requests to `faustwp/v1/validate_secret_key`.
*
* Authorized if the 'secret_key' settings value and http header 'x-faustwp-secret' match.
*
* @link https://developer.wordpress.org/reference/functions/register_rest_route/
* @link https://developer.wordpress.org/rest-api/extending-the-rest-api/routes-and-endpoints/#permissions-callback
*
* @param \WP_REST_Request $request The current \WP_REST_Request object.
*
* @return bool True if current user can, false if else.
*/
function rest_validate_secret_key_permission_callback( \WP_REST_Request $request ) {
return rest_authorize_permission_callback( $request );
}
Loading