Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion packages/join-block/src/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,9 @@ public static function init()
if (!$log) {
$log = 'Could not load error log. Please contact Common Knowledge support.';
}
return "<pre style=\"max-width:150ch\">$log</pre>";
// The log holds text sent by visitors (join and step requests), so
// it must be shown as text, never as markup.
return '<pre style="max-width:150ch">' . esc_html($log) . '</pre>';
});
/** @var Html_Field $logRetentionField */
$logRetentionField = Field::make('html', 'ck_join_flow_log_retention');
Expand Down
26 changes: 26 additions & 0 deletions packages/join-e2e/tests/log-viewer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { test, expect } from '@playwright/test';
import { loginAsAdmin } from './helpers';

/**
* The plugin's log, shown on WP Admin > Join > Logging, holds text that
* visitors send. It must be shown as text: markup in it must never run in the
* admin's browser.
*/
test('markup sent by a visitor is shown as text in the log viewer, not run', async ({ page, request }) => {
const probe = `log-viewer-probe-${Date.now()}`;
const markup = `<img id="${probe}" src="x" onerror="window.${probe.replace(/-/g, '_')}=true">`;

// A join on a plan that doesn't exist logs the plan's ID as sent.
const response = await request.post('/wp-json/join/v1/join', {
data: { email: 'someone@example.com', membership: markup },
});
expect(response.status()).toBe(500);

await loginAsAdmin(page);
await page.goto('/wp-admin/admin.php?page=crb_carbon_fields_container_ck_join_flow.php');
await page.getByRole('tab', { name: 'Logging', exact: true }).click();

await expect(page.locator('pre', { hasText: `Invalid membership plan: ${markup}` })).toBeVisible();
await expect(page.locator(`#${probe}`)).toHaveCount(0);
expect(await page.evaluate((name) => (window as any)[name], probe.replace(/-/g, '_'))).toBeUndefined();
});
Loading