@aws-amplify/ui-react 6.4.0 → 6.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Field-4b189104.js → Field-3db91851.js} +23 -12
- package/dist/{ThemeStyle-b2dce96a.js → ThemeStyle-7d5abbc4.js} +18 -12
- package/dist/esm/components/ThemeProvider/ComponentStyle.mjs +21 -0
- package/dist/esm/components/ThemeProvider/GlobalStyle.mjs +18 -0
- package/dist/esm/components/ThemeProvider/Style.mjs +62 -0
- package/dist/esm/components/ThemeProvider/ThemeStyle.mjs +7 -60
- package/dist/esm/server.mjs +2 -0
- package/dist/esm/version.mjs +1 -1
- package/dist/index.js +79 -80
- package/dist/internal.js +1 -2
- package/dist/server.js +61 -13
- package/dist/styles/breadcrumbs.css +4 -2
- package/dist/styles/breadcrumbs.layer.css +4 -2
- package/dist/styles/button.css +19 -10
- package/dist/styles/button.layer.css +19 -10
- package/dist/styles/input.css +3 -2
- package/dist/styles/input.layer.css +3 -2
- package/dist/styles/link.css +10 -5
- package/dist/styles/link.layer.css +10 -5
- package/dist/styles/reset.css +4 -1
- package/dist/styles/reset.layer.css +4 -1
- package/dist/styles/sliderField.css +3 -2
- package/dist/styles/sliderField.layer.css +3 -2
- package/dist/styles/textArea.css +3 -2
- package/dist/styles/textArea.layer.css +3 -2
- package/dist/styles.css +42 -23
- package/dist/styles.layer.css +42 -23
- package/dist/types/components/ThemeProvider/ComponentStyle.d.ts +22 -0
- package/dist/types/components/ThemeProvider/GlobalStyle.d.ts +20 -0
- package/dist/types/components/ThemeProvider/Style.d.ts +13 -0
- package/dist/types/components/ThemeProvider/ThemeStyle.d.ts +6 -4
- package/dist/types/primitives/shared/responsive/utils.d.ts +1 -1
- package/dist/types/server.d.ts +2 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +5 -4
- package/dist/primitiveWithForwardRef-7e929242.js +0 -36
- package/dist/types/primitives/Card/__test__/Card.test.d.ts +0 -1
|
@@ -5,7 +5,6 @@ var core = require('@aws-amplify/core');
|
|
|
5
5
|
var auth = require('aws-amplify/auth');
|
|
6
6
|
var uiReactCore = require('@aws-amplify/ui-react-core');
|
|
7
7
|
var ui = require('@aws-amplify/ui');
|
|
8
|
-
var primitiveWithForwardRef = require('./primitiveWithForwardRef-7e929242.js');
|
|
9
8
|
|
|
10
9
|
function _interopNamespace(e) {
|
|
11
10
|
if (e && e.__esModule) return e;
|
|
@@ -636,6 +635,17 @@ const useStyles = (props, style) => {
|
|
|
636
635
|
}), [propStyles, style, breakpoints, breakpoint, tokens]);
|
|
637
636
|
};
|
|
638
637
|
|
|
638
|
+
/**
|
|
639
|
+
* Updates the return type for primitives wrapped in `React.forwardRef` to
|
|
640
|
+
* `React.ReactElement`. In React 18 the return type of `React.ExoticComponent`
|
|
641
|
+
* was changed from `React.ReactElement` to `React.ReactNode`, which breaks
|
|
642
|
+
* clients using React 16 and 17.
|
|
643
|
+
*
|
|
644
|
+
* @param primitive UI Primitive to be wrapped with `React.forwardRef`
|
|
645
|
+
* @returns ForwaredRef wrapped UI Primitive
|
|
646
|
+
*/
|
|
647
|
+
const primitiveWithForwardRef = (primitive) => React__namespace.forwardRef(primitive);
|
|
648
|
+
|
|
639
649
|
const ViewPrimitive = ({ as = 'div', children, testId, ariaLabel, isDisabled, style, inert, ...rest }, ref) => {
|
|
640
650
|
const { propStyles, nonStyleProps } = useStyles(rest, style);
|
|
641
651
|
return React__namespace.createElement(as, {
|
|
@@ -651,7 +661,7 @@ const ViewPrimitive = ({ as = 'div', children, testId, ariaLabel, isDisabled, st
|
|
|
651
661
|
/**
|
|
652
662
|
* [📖 Docs](https://ui.docs.amplify.aws/react/components/view)
|
|
653
663
|
*/
|
|
654
|
-
const View = primitiveWithForwardRef
|
|
664
|
+
const View = primitiveWithForwardRef(ViewPrimitive);
|
|
655
665
|
View.displayName = 'View';
|
|
656
666
|
|
|
657
667
|
const defaultViewBox = { minX: 0, minY: 0, width: 24, height: 24 };
|
|
@@ -681,7 +691,7 @@ as = 'svg', fill = 'currentColor', pathData, viewBox = defaultViewBox, children,
|
|
|
681
691
|
/**
|
|
682
692
|
* [📖 Docs](https://ui.docs.amplify.aws/react/components/icon)
|
|
683
693
|
*/
|
|
684
|
-
const Icon = primitiveWithForwardRef
|
|
694
|
+
const Icon = primitiveWithForwardRef(IconPrimitive);
|
|
685
695
|
Icon.displayName = 'Icon';
|
|
686
696
|
|
|
687
697
|
const IconsContext = React__namespace.createContext({});
|
|
@@ -994,7 +1004,7 @@ function useDropZone({ onDropComplete, onDragEnter: _onDragEnter, onDragLeave: _
|
|
|
994
1004
|
const FieldGroupIconPrimitive = ({ className, children, isVisible = true, excludeFromTabOrder = false, ...rest }, ref) => {
|
|
995
1005
|
return isVisible ? (React__namespace.createElement(View, { className: ui.classNames(ui.ComponentClassName.FieldGroupIcon, className), ref: ref, tabIndex: excludeFromTabOrder ? -1 : undefined, ...rest }, children)) : null;
|
|
996
1006
|
};
|
|
997
|
-
const FieldGroupIcon = primitiveWithForwardRef
|
|
1007
|
+
const FieldGroupIcon = primitiveWithForwardRef(FieldGroupIconPrimitive);
|
|
998
1008
|
FieldGroupIcon.displayName = 'FieldGroupIcon';
|
|
999
1009
|
|
|
1000
1010
|
const FieldsetContext = React__namespace.createContext({
|
|
@@ -1011,7 +1021,7 @@ const FlexPrimitive = ({ className, children, ...rest }, ref) => (React__namespa
|
|
|
1011
1021
|
/**
|
|
1012
1022
|
* [📖 Docs](https://ui.docs.amplify.aws/react/components/flex)
|
|
1013
1023
|
*/
|
|
1014
|
-
const Flex = primitiveWithForwardRef
|
|
1024
|
+
const Flex = primitiveWithForwardRef(FlexPrimitive);
|
|
1015
1025
|
Flex.displayName = 'Flex';
|
|
1016
1026
|
|
|
1017
1027
|
const LINEAR_EMPTY = 'linear-empty';
|
|
@@ -1061,7 +1071,7 @@ const LoaderPrimitive = ({ className, filledColor, emptyColor, size, variation,
|
|
|
1061
1071
|
/**
|
|
1062
1072
|
* [📖 Docs](https://ui.docs.amplify.aws/react/components/loader)
|
|
1063
1073
|
*/
|
|
1064
|
-
const Loader = primitiveWithForwardRef
|
|
1074
|
+
const Loader = primitiveWithForwardRef(LoaderPrimitive);
|
|
1065
1075
|
Loader.displayName = 'Loader';
|
|
1066
1076
|
|
|
1067
1077
|
// These variations support colorThemes. 'undefined' accounts for our
|
|
@@ -1085,11 +1095,11 @@ const ButtonPrimitive = ({ className, children, colorTheme, isFullWidth = false,
|
|
|
1085
1095
|
/**
|
|
1086
1096
|
* [📖 Docs](https://ui.docs.amplify.aws/react/components/button)
|
|
1087
1097
|
*/
|
|
1088
|
-
const Button = primitiveWithForwardRef
|
|
1098
|
+
const Button = primitiveWithForwardRef(ButtonPrimitive);
|
|
1089
1099
|
Button.displayName = 'Button';
|
|
1090
1100
|
|
|
1091
1101
|
const FieldGroupIconButtonPrimitive = ({ children, className, ...rest }, ref) => (React__namespace.createElement(FieldGroupIcon, { as: Button, className: ui.classNames(ui.ComponentClassName.FieldGroupIconButton, className), ref: ref, ...rest }, children));
|
|
1092
|
-
const FieldGroupIconButton = primitiveWithForwardRef
|
|
1102
|
+
const FieldGroupIconButton = primitiveWithForwardRef(FieldGroupIconButtonPrimitive);
|
|
1093
1103
|
FieldGroupIconButton.displayName = 'FieldGroupIconButton';
|
|
1094
1104
|
|
|
1095
1105
|
const ariaLabelText = ComponentText.Fields.clearButtonLabel;
|
|
@@ -1097,7 +1107,7 @@ const FieldClearButtonPrimitive = ({ ariaLabel = ariaLabelText, size, ...rest },
|
|
|
1097
1107
|
const icons = useIcons('field');
|
|
1098
1108
|
return (React__namespace.createElement(FieldGroupIconButton, { ariaLabel: ariaLabel, size: size, ref: ref, ...rest }, icons?.clear ?? React__namespace.createElement(IconClose, null)));
|
|
1099
1109
|
};
|
|
1100
|
-
const FieldClearButton = primitiveWithForwardRef
|
|
1110
|
+
const FieldClearButton = primitiveWithForwardRef(FieldClearButtonPrimitive);
|
|
1101
1111
|
FieldClearButton.displayName = 'FieldClearButton';
|
|
1102
1112
|
|
|
1103
1113
|
const TextPrimitive = ({ as = 'p', className, children, isTruncated, variation, ...rest }, ref) => {
|
|
@@ -1107,7 +1117,7 @@ const TextPrimitive = ({ as = 'p', className, children, isTruncated, variation,
|
|
|
1107
1117
|
/**
|
|
1108
1118
|
* [📖 Docs](https://ui.docs.amplify.aws/react/components/text)
|
|
1109
1119
|
*/
|
|
1110
|
-
const Text = primitiveWithForwardRef
|
|
1120
|
+
const Text = primitiveWithForwardRef(TextPrimitive);
|
|
1111
1121
|
Text.displayName = 'Text';
|
|
1112
1122
|
|
|
1113
1123
|
const QA_FIELD_DESCRIPTION = 'qa-field-description';
|
|
@@ -1126,7 +1136,7 @@ const LabelPrimitive = ({ children, className, visuallyHidden, ...rest }, ref) =
|
|
|
1126
1136
|
[ui.ComponentClassName.VisuallyHidden]: visuallyHidden,
|
|
1127
1137
|
}), ref: ref, ...rest }, children));
|
|
1128
1138
|
};
|
|
1129
|
-
const Label = primitiveWithForwardRef
|
|
1139
|
+
const Label = primitiveWithForwardRef(LabelPrimitive);
|
|
1130
1140
|
Label.displayName = 'Label';
|
|
1131
1141
|
|
|
1132
1142
|
const FieldPrimitive = (props, ref) => {
|
|
@@ -1137,7 +1147,7 @@ const FieldPrimitive = (props, ref) => {
|
|
|
1137
1147
|
children,
|
|
1138
1148
|
errorMessage ? (React__namespace.createElement(FieldErrorMessage, { hasError: hasError, errorMessage: errorMessage })) : null));
|
|
1139
1149
|
};
|
|
1140
|
-
const Field = primitiveWithForwardRef
|
|
1150
|
+
const Field = primitiveWithForwardRef(FieldPrimitive);
|
|
1141
1151
|
Field.displayName = 'Field';
|
|
1142
1152
|
|
|
1143
1153
|
exports.ARROW_DOWN = ARROW_DOWN;
|
|
@@ -1185,6 +1195,7 @@ exports.View = View;
|
|
|
1185
1195
|
exports.getConsecutiveIntArray = getConsecutiveIntArray;
|
|
1186
1196
|
exports.getStyleValue = getStyleValue;
|
|
1187
1197
|
exports.getValueAtCurrentBreakpoint = getValueAtCurrentBreakpoint;
|
|
1198
|
+
exports.primitiveWithForwardRef = primitiveWithForwardRef;
|
|
1188
1199
|
exports.strHasLength = strHasLength;
|
|
1189
1200
|
exports.useAuth = useAuth;
|
|
1190
1201
|
exports.useBreakpoint = useBreakpoint;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var React = require('react');
|
|
4
|
-
var primitiveWithForwardRef = require('./primitiveWithForwardRef-7e929242.js');
|
|
5
4
|
|
|
6
5
|
function _interopNamespace(e) {
|
|
7
6
|
if (e && e.__esModule) return e;
|
|
@@ -23,10 +22,11 @@ function _interopNamespace(e) {
|
|
|
23
22
|
|
|
24
23
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
/**
|
|
26
|
+
* @experimental
|
|
27
|
+
* [📖 Docs](https://ui.docs.amplify.aws/react/components/theme)
|
|
28
|
+
*/
|
|
29
|
+
const Style = ({ cssText, ...rest }) => {
|
|
30
30
|
/*
|
|
31
31
|
Only inject theme CSS variables if given a theme.
|
|
32
32
|
The CSS file users import already has the default theme variables in it.
|
|
@@ -72,20 +72,26 @@ const ThemeStylePrimitive = ({ theme, nonce, ...rest }, ref) => {
|
|
|
72
72
|
Therefore, by only rendering CSS text which does not include a closing '</style>' tag,
|
|
73
73
|
we ensure that the browser will correctly interpret all the text as CSS.
|
|
74
74
|
*/
|
|
75
|
-
if (/<\/style/i.test(cssText)) {
|
|
75
|
+
if (cssText === undefined || /<\/style/i.test(cssText)) {
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
dangerouslySetInnerHTML: { __html: cssText }, nonce: nonce }));
|
|
82
|
-
}
|
|
78
|
+
return (React__namespace.createElement("style", { ...rest,
|
|
79
|
+
// eslint-disable-next-line react/no-danger
|
|
80
|
+
dangerouslySetInnerHTML: { __html: cssText } }));
|
|
83
81
|
};
|
|
82
|
+
Style.displayName = 'Style';
|
|
83
|
+
|
|
84
84
|
/**
|
|
85
85
|
* @experimental
|
|
86
86
|
* [📖 Docs](https://ui.docs.amplify.aws/react/components/theme)
|
|
87
87
|
*/
|
|
88
|
-
const ThemeStyle =
|
|
88
|
+
const ThemeStyle = ({ theme, ...rest }) => {
|
|
89
|
+
if (!theme)
|
|
90
|
+
return null;
|
|
91
|
+
const { name, cssText } = theme;
|
|
92
|
+
return React__namespace.createElement(Style, { ...rest, cssText: cssText, id: `amplify-theme-${name}` });
|
|
93
|
+
};
|
|
89
94
|
ThemeStyle.displayName = 'ThemeStyle';
|
|
90
95
|
|
|
96
|
+
exports.Style = Style;
|
|
91
97
|
exports.ThemeStyle = ThemeStyle;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createComponentCSS } from '@aws-amplify/ui';
|
|
3
|
+
import { Style } from './Style.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @experimental
|
|
7
|
+
* [📖 Docs](https://ui.docs.amplify.aws/react/components/theme)
|
|
8
|
+
*/
|
|
9
|
+
const ComponentStyle = ({ theme, componentThemes = [], ...rest }) => {
|
|
10
|
+
if (!theme || !componentThemes.length) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const cssText = createComponentCSS({
|
|
14
|
+
theme,
|
|
15
|
+
components: componentThemes,
|
|
16
|
+
});
|
|
17
|
+
return React.createElement(Style, { ...rest, cssText: cssText });
|
|
18
|
+
};
|
|
19
|
+
ComponentStyle.displayName = 'ComponentStyle';
|
|
20
|
+
|
|
21
|
+
export { ComponentStyle };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createGlobalCSS } from '@aws-amplify/ui';
|
|
3
|
+
import { Style } from './Style.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @experimental
|
|
7
|
+
* [📖 Docs](https://ui.docs.amplify.aws/react/components/theme)
|
|
8
|
+
*/
|
|
9
|
+
const GlobalStyle = ({ styles, ...rest }) => {
|
|
10
|
+
if (!styles) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const cssText = createGlobalCSS(styles);
|
|
14
|
+
return React.createElement(Style, { ...rest, cssText: cssText });
|
|
15
|
+
};
|
|
16
|
+
GlobalStyle.displayName = 'GlobalStyle';
|
|
17
|
+
|
|
18
|
+
export { GlobalStyle };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @experimental
|
|
5
|
+
* [📖 Docs](https://ui.docs.amplify.aws/react/components/theme)
|
|
6
|
+
*/
|
|
7
|
+
const Style = ({ cssText, ...rest }) => {
|
|
8
|
+
/*
|
|
9
|
+
Only inject theme CSS variables if given a theme.
|
|
10
|
+
The CSS file users import already has the default theme variables in it.
|
|
11
|
+
This will allow users to use the provider and theme with CSS variables
|
|
12
|
+
without having to worry about specificity issues because this stylesheet
|
|
13
|
+
will likely come after a user's defined CSS.
|
|
14
|
+
|
|
15
|
+
Q: Why are we using dangerouslySetInnerHTML?
|
|
16
|
+
A: We need to directly inject the theme's CSS string into the <style> tag without typical HTML escaping.
|
|
17
|
+
For example, JSX would escape characters meaningful in CSS such as ', ", < and >, thus breaking the CSS.
|
|
18
|
+
Q: Why not use a sanitization library such as DOMPurify?
|
|
19
|
+
A: For our use case, we specifically want to purify CSS text, *not* HTML.
|
|
20
|
+
DOMPurify, as well as any other HTML sanitization library, would escape/encode meaningful CSS characters
|
|
21
|
+
and break our CSS in the same way that JSX would.
|
|
22
|
+
|
|
23
|
+
Q: Are there any security risks in this particular use case?
|
|
24
|
+
A: Anything set inside of a <style> tag is always interpreted as CSS text, *not* HTML.
|
|
25
|
+
Reference: “Restrictions on the content of raw text elements” https://html.spec.whatwg.org/dev/syntax.html#cdata-rcdata-restrictions
|
|
26
|
+
And in our case, we are using dangerouslySetInnerHTML to set CSS text inside of a <style> tag.
|
|
27
|
+
|
|
28
|
+
Thus, it really comes down to the question: Could a malicious user escape from the context of the <style> tag?
|
|
29
|
+
For example, when inserting HTML into the DOM, could someone prematurely close the </style> tag and add a <script> tag?
|
|
30
|
+
e.g., </style><script>alert('hello')</script>
|
|
31
|
+
The answer depends on whether the code is rendered on the client or server side.
|
|
32
|
+
|
|
33
|
+
Client side
|
|
34
|
+
- To prevent XSS inside of the <style> tag, we need to make sure it's not closed prematurely.
|
|
35
|
+
- This is prevented by React because React creates a style DOM node (e.g., React.createElement(‘style’, ...)), and directly sets innerHTML as a string.
|
|
36
|
+
- Even if the string contains a closing </style> tag, it will still be interpreted as CSS text by the browser.
|
|
37
|
+
- Therefore, there is not an XSS vulnerability on the client side.
|
|
38
|
+
|
|
39
|
+
Server side
|
|
40
|
+
- When React code is rendered on the server side (e.g., NextJS), the code is sent to the browser as HTML text.
|
|
41
|
+
- Therefore, it *IS* possible to insert a closing </style> tag and escape the CSS context, which opens an XSS vulnerability.
|
|
42
|
+
|
|
43
|
+
Q: How are we mitigating the potential attack vector?
|
|
44
|
+
A: To fix this potential attack vector on the server side, we need to filter out any closing </style> tags,
|
|
45
|
+
as this the only way to escape from the context of the browser interpreting the text as CSS.
|
|
46
|
+
We also need to catch cases where there is any kind of whitespace character </style[HERE]>, such as tabs, carriage returns, etc:
|
|
47
|
+
</style
|
|
48
|
+
|
|
49
|
+
>
|
|
50
|
+
Therefore, by only rendering CSS text which does not include a closing '</style>' tag,
|
|
51
|
+
we ensure that the browser will correctly interpret all the text as CSS.
|
|
52
|
+
*/
|
|
53
|
+
if (cssText === undefined || /<\/style/i.test(cssText)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return (React.createElement("style", { ...rest,
|
|
57
|
+
// eslint-disable-next-line react/no-danger
|
|
58
|
+
dangerouslySetInnerHTML: { __html: cssText } }));
|
|
59
|
+
};
|
|
60
|
+
Style.displayName = 'Style';
|
|
61
|
+
|
|
62
|
+
export { Style };
|
|
@@ -1,69 +1,16 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Style } from './Style.mjs';
|
|
3
3
|
|
|
4
|
-
const ThemeStylePrimitive = ({ theme, nonce, ...rest }, ref) => {
|
|
5
|
-
if (!theme)
|
|
6
|
-
return null;
|
|
7
|
-
const { name, cssText } = theme;
|
|
8
|
-
/*
|
|
9
|
-
Only inject theme CSS variables if given a theme.
|
|
10
|
-
The CSS file users import already has the default theme variables in it.
|
|
11
|
-
This will allow users to use the provider and theme with CSS variables
|
|
12
|
-
without having to worry about specificity issues because this stylesheet
|
|
13
|
-
will likely come after a user's defined CSS.
|
|
14
|
-
|
|
15
|
-
Q: Why are we using dangerouslySetInnerHTML?
|
|
16
|
-
A: We need to directly inject the theme's CSS string into the <style> tag without typical HTML escaping.
|
|
17
|
-
For example, JSX would escape characters meaningful in CSS such as ', ", < and >, thus breaking the CSS.
|
|
18
|
-
Q: Why not use a sanitization library such as DOMPurify?
|
|
19
|
-
A: For our use case, we specifically want to purify CSS text, *not* HTML.
|
|
20
|
-
DOMPurify, as well as any other HTML sanitization library, would escape/encode meaningful CSS characters
|
|
21
|
-
and break our CSS in the same way that JSX would.
|
|
22
|
-
|
|
23
|
-
Q: Are there any security risks in this particular use case?
|
|
24
|
-
A: Anything set inside of a <style> tag is always interpreted as CSS text, *not* HTML.
|
|
25
|
-
Reference: “Restrictions on the content of raw text elements” https://html.spec.whatwg.org/dev/syntax.html#cdata-rcdata-restrictions
|
|
26
|
-
And in our case, we are using dangerouslySetInnerHTML to set CSS text inside of a <style> tag.
|
|
27
|
-
|
|
28
|
-
Thus, it really comes down to the question: Could a malicious user escape from the context of the <style> tag?
|
|
29
|
-
For example, when inserting HTML into the DOM, could someone prematurely close the </style> tag and add a <script> tag?
|
|
30
|
-
e.g., </style><script>alert('hello')</script>
|
|
31
|
-
The answer depends on whether the code is rendered on the client or server side.
|
|
32
|
-
|
|
33
|
-
Client side
|
|
34
|
-
- To prevent XSS inside of the <style> tag, we need to make sure it's not closed prematurely.
|
|
35
|
-
- This is prevented by React because React creates a style DOM node (e.g., React.createElement(‘style’, ...)), and directly sets innerHTML as a string.
|
|
36
|
-
- Even if the string contains a closing </style> tag, it will still be interpreted as CSS text by the browser.
|
|
37
|
-
- Therefore, there is not an XSS vulnerability on the client side.
|
|
38
|
-
|
|
39
|
-
Server side
|
|
40
|
-
- When React code is rendered on the server side (e.g., NextJS), the code is sent to the browser as HTML text.
|
|
41
|
-
- Therefore, it *IS* possible to insert a closing </style> tag and escape the CSS context, which opens an XSS vulnerability.
|
|
42
|
-
|
|
43
|
-
Q: How are we mitigating the potential attack vector?
|
|
44
|
-
A: To fix this potential attack vector on the server side, we need to filter out any closing </style> tags,
|
|
45
|
-
as this the only way to escape from the context of the browser interpreting the text as CSS.
|
|
46
|
-
We also need to catch cases where there is any kind of whitespace character </style[HERE]>, such as tabs, carriage returns, etc:
|
|
47
|
-
</style
|
|
48
|
-
|
|
49
|
-
>
|
|
50
|
-
Therefore, by only rendering CSS text which does not include a closing '</style>' tag,
|
|
51
|
-
we ensure that the browser will correctly interpret all the text as CSS.
|
|
52
|
-
*/
|
|
53
|
-
if (/<\/style/i.test(cssText)) {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
return (React.createElement("style", { ...rest, ref: ref, id: `amplify-theme-${name}`,
|
|
58
|
-
// eslint-disable-next-line react/no-danger
|
|
59
|
-
dangerouslySetInnerHTML: { __html: cssText }, nonce: nonce }));
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
4
|
/**
|
|
63
5
|
* @experimental
|
|
64
6
|
* [📖 Docs](https://ui.docs.amplify.aws/react/components/theme)
|
|
65
7
|
*/
|
|
66
|
-
const ThemeStyle =
|
|
8
|
+
const ThemeStyle = ({ theme, ...rest }) => {
|
|
9
|
+
if (!theme)
|
|
10
|
+
return null;
|
|
11
|
+
const { name, cssText } = theme;
|
|
12
|
+
return React.createElement(Style, { ...rest, cssText: cssText, id: `amplify-theme-${name}` });
|
|
13
|
+
};
|
|
67
14
|
ThemeStyle.displayName = 'ThemeStyle';
|
|
68
15
|
|
|
69
16
|
export { ThemeStyle };
|
package/dist/esm/server.mjs
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { ThemeStyle } from './components/ThemeProvider/ThemeStyle.mjs';
|
|
2
|
+
export { ComponentStyle } from './components/ThemeProvider/ComponentStyle.mjs';
|
|
3
|
+
export { GlobalStyle } from './components/ThemeProvider/GlobalStyle.mjs';
|
|
2
4
|
export { createComponentClasses, createTheme, defaultDarkModeOverride, defaultTheme, defineComponentTheme } from '@aws-amplify/ui';
|
package/dist/esm/version.mjs
CHANGED