Skip to content
Merged
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: 1 addition & 3 deletions src/StyleContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,9 @@ export interface StyleContextProps {
linters?: Linter[];
/** Wrap css in a layer to avoid global style conflict */
layer?: boolean;

/** Hardcode here since transformer not support take effect on serialize currently */
autoPrefix?: boolean;

/** Nonce for CSP (Content Security Policy) */
nonce?: string | (() => string);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The removal of the nonce property from StyleContextProps is a security regression. This property is essential for applications that implement a strict Content Security Policy (CSP) requiring nonces for inline styles. By removing this property, the library loses the ability to pass nonces to dynamically injected style tags, which will cause them to be blocked by the browser in strict CSP environments. This may force developers to adopt less secure CSP directives like 'unsafe-inline' to maintain functionality.


const StyleContext = React.createContext<StyleContextProps>({
Expand Down
20 changes: 7 additions & 13 deletions src/hooks/useCSSVarRegister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import StyleContext, {
ATTR_TOKEN,
CSS_IN_JS_INSTANCE,
} from '../StyleContext';
import { isClientSide, mergeCSSConfig, toStyleStr } from '../util';
import { isClientSide, toStyleStr } from '../util';
import type { TokenWithCSSVar } from '../util/css-variables';
import { transformToken } from '../util/css-variables';
import type { ExtractStyle } from './useGlobalCache';
Expand Down Expand Up @@ -39,7 +39,6 @@ const useCSSVarRegister = <V, T extends Record<string, V>>(
cache: { instanceId },
container,
hashPriority,
nonce,
} = useContext(StyleContext);
const { _tokenKey: tokenKey } = token;

Expand Down Expand Up @@ -71,17 +70,12 @@ const useCSSVarRegister = <V, T extends Record<string, V>>(
if (!cssVarsStr) {
return;
}
const mergedCSSConfig = mergeCSSConfig<Parameters<typeof updateCSS>[2]>(
{
mark: ATTR_MARK,
prepend: 'queue',
attachTo: container,
priority: -999,
},
nonce,
);

const style = updateCSS(cssVarsStr, styleId, mergedCSSConfig);
const style = updateCSS(cssVarsStr, styleId, {
mark: ATTR_MARK,
prepend: 'queue',
attachTo: container,
priority: -999,
});
Comment on lines +73 to +78
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The cssVarsStr variable, which contains CSS variable definitions derived from theme tokens, is passed directly to the updateCSS sink without sanitization. If token values are user-controlled (e.g., in a customizable theme), an attacker can inject arbitrary CSS rules by including characters like ; or } in the token value. This can lead to UI redressing or data exfiltration via CSS-based attacks.


(style as any)[CSS_IN_JS_INSTANCE] = instanceId;

Expand Down
20 changes: 7 additions & 13 deletions src/hooks/useCacheToken.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import StyleContext, {
CSS_IN_JS_INSTANCE,
} from '../StyleContext';
import type Theme from '../theme/Theme';
import { flattenToken, memoResult, mergeCSSConfig, token2key, toStyleStr } from '../util';
import { flattenToken, memoResult, token2key, toStyleStr } from '../util';
import { transformToken } from '../util/css-variables';
import type { ExtractStyle } from './useGlobalCache';
import useGlobalCache from './useGlobalCache';
Expand Down Expand Up @@ -161,7 +161,6 @@ export default function useCacheToken<
cache: { instanceId },
container,
hashPriority,
nonce,
} = useContext(StyleContext);
const {
salt = '',
Expand Down Expand Up @@ -220,17 +219,12 @@ export default function useCacheToken<
if (!cssVarsStr) {
return;
}
const mergedCSSConfig = mergeCSSConfig<Parameters<typeof updateCSS>[2]>(
{
mark: ATTR_MARK,
prepend: 'queue',
attachTo: container,
priority: -999,
},
nonce,
);

const style = updateCSS(cssVarsStr, hash(`css-var-${themeKey}`), mergedCSSConfig);
const style = updateCSS(cssVarsStr, hash(`css-var-${themeKey}`), {
mark: ATTR_MARK,
prepend: 'queue',
attachTo: container,
priority: -999,
});
Comment on lines +222 to +227
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

Theme token values are injected into the document via updateCSS without sanitization. This allows for CSS injection if the theme tokens or overrides contain malicious content. An attacker could exploit this to manipulate the page's appearance or exfiltrate sensitive information using CSS selectors and external resources.


(style as any)[CSS_IN_JS_INSTANCE] = instanceId;

Expand Down
22 changes: 0 additions & 22 deletions src/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,28 +155,6 @@ export function supportLogicProps(): boolean {

export const isClientSide = canUseDom();

/**
* Merge CSS injection configuration with nonce support
*/
/**
* Merge CSS injection configuration with nonce support
*/
export function mergeCSSConfig<T>(
config: T,
nonce?: string | (() => string),
): T {
if (!nonce) {
return config;
}

const mergedConfig = { ...config };
const nonceStr = typeof nonce === 'function' ? nonce() : nonce;
if (nonceStr) {
(mergedConfig as any).csp = { nonce: nonceStr };
}

return mergedConfig;
}
export function unit(num: string | number) {
if (typeof num === 'number') {
return `${num}px`;
Expand Down
22 changes: 0 additions & 22 deletions tests/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -507,28 +507,6 @@ describe('csssinjs', () => {
test('function', () => 'bamboo');
});

describe('StyleProvider nonce for CSS var', () => {
function testWithStyleProvider(name: string, nonce: string | (() => string)) {
it(name, () => {
const { unmount } = render(
<StyleProvider cache={createCache()} nonce={nonce}>
<Box />
</StyleProvider>,
);

const styles = Array.from(document.head.querySelectorAll('style'));
// Box 组件使用 useCacheToken 注册 CSS var,应该有 nonce
const cssVarStyle = styles.find(s => s.innerHTML.includes('--primary-color'));
expect(cssVarStyle).toBeDefined();
expect(cssVarStyle?.nonce).toBe('bamboo');
// unmount 后样式清理行为取决于 cache 配置
});
}

testWithStyleProvider('string', 'bamboo');
testWithStyleProvider('function', () => 'bamboo');
});

it('should not insert style with different instanceId', () => {
const genDemoStyle = (token: DerivativeToken): CSSInterpolation => ({
div: {
Expand Down