Add test for "fetch redirect", add CSS value validation for external render (#37207) (#37216)

Backport #37207
This commit is contained in:
wxiaoguang
2026-04-15 02:25:57 +08:00
committed by GitHub
parent 2aca966c5f
commit 5d852d2d0a
7 changed files with 107 additions and 7 deletions

View File

@@ -0,0 +1,25 @@
import './external-render-helper.ts';
test('isValidCssColor', async () => {
const isValidCssColor = window.testModules.externalRenderHelper!.isValidCssColor;
expect(isValidCssColor(null)).toBe(false);
expect(isValidCssColor('')).toBe(false);
expect(isValidCssColor('#123')).toBe(true);
expect(isValidCssColor('#1234')).toBe(true);
expect(isValidCssColor('#abcabc')).toBe(true);
expect(isValidCssColor('#abcabc12')).toBe(true);
expect(isValidCssColor('rgb(255 255 255)')).toBe(true);
expect(isValidCssColor('rgb(0, 255, 255)')).toBe(true);
// examples from MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/rgb
expect(isValidCssColor('rgb(255 255 255 / 50%)')).toBe(true);
expect(isValidCssColor('rgb(from #123456 hwb(120deg 10% 20%) calc(g + 40) b / 0.5)')).toBe(true);
expect(isValidCssColor('#123 ; other')).toBe(false);
expect(isValidCssColor('#123 : other')).toBe(false);
expect(isValidCssColor('#rgb(0, 255, 255); other')).toBe(false);
expect(isValidCssColor('#rgb(0, 255, 255)} other')).toBe(false);
expect(isValidCssColor('url(other)')).toBe(false);
});

View File

@@ -15,6 +15,17 @@ RENDER_COMMAND = `echo '<div style="width: 100%; height: 2000px; border: 10px so
*/
// Check whether the user-provided color value is a valid CSS color format to avoid CSS injection.
// Don't extract this function to a common module, because this file is an IIFE module for external render
// and should not have any dependency to avoid potential conflicts.
function isValidCssColor(s: string | null): boolean {
if (!s) return false;
// it should only be in format "#hex" or "rgb(...)", because it comes from a computed style's color value
const reHex = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
const reRgb = /^rgb\([^{}'";:]+\)$/;
return reHex.test(s) || reRgb.test(s);
}
const url = new URL(window.location.href);
const isDarkTheme = url.searchParams.get('gitea-is-dark-theme') === 'true';
@@ -23,7 +34,7 @@ if (isDarkTheme) {
}
const backgroundColor = url.searchParams.get('gitea-iframe-bgcolor');
if (backgroundColor) {
if (isValidCssColor(backgroundColor)) {
// create a style element to set background color, then it can be overridden by the content page's own style if needed
const style = document.createElement('style');
style.textContent = `
@@ -75,3 +86,7 @@ if (iframeId) {
}
});
}
if (window.testModules) {
window.testModules.externalRenderHelper = {isValidCssColor};
}

View File

@@ -156,8 +156,8 @@ export function checkAppUrl() {
if (curUrl.startsWith(appUrl) || `${curUrl}/` === appUrl) {
return;
}
showGlobalErrorMessage(`Your ROOT_URL in app.ini is "${appUrl}", it's unlikely matching the site you are visiting.
Mismatched ROOT_URL config causes wrong URL links for web UI/mail content/webhook notification/OAuth2 sign-in.`, 'warning');
showGlobalErrorMessage(`The detected web site URL is "${appUrl}", it's unlikely matching the site config.
Mismatched app.ini ROOT_URL or reverse proxy "Host/X-Forwarded-Proto" config might cause wrong URL links for web UI/mail content/webhook notification/OAuth2 sign-in.`, 'warning');
}
export function checkAppUrlScheme() {

View File

@@ -68,6 +68,15 @@ interface Window {
turnstile: any,
hcaptcha: any,
// Make IIFE private functions can be tested in unit tests, without exposing the IIFE module to global scope.
// Otherwise, when using "export" in IIFE code, the compiled JS will inject global "var externalRenderHelper = ..."
// which is not expected and may cause conflicts with other modules.
testModules: {
externalRenderHelper?: {
isValidCssColor(s: string | null): boolean,
}
}
// do not add more properties here unless it is a must
}

View File

@@ -24,4 +24,6 @@ window.config = {
i18n: {},
};
window.testModules = {};
export {}; // mark as module for top-level await