@transferwise/components 0.0.0-experimental-bcea1dd → 0.0.0-experimental-1168b1c
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/build/Calendar/Calendar.js +47 -8
- package/build/Calendar/Calendar.js.map +1 -1
- package/build/Calendar/Calendar.messages.js +3 -0
- package/build/Calendar/Calendar.messages.js.map +1 -1
- package/build/Calendar/Calendar.messages.mjs +3 -0
- package/build/Calendar/Calendar.messages.mjs.map +1 -1
- package/build/Calendar/Calendar.mjs +48 -9
- package/build/Calendar/Calendar.mjs.map +1 -1
- package/build/Calendar/DayCalendar/DayCalendar.js +4 -0
- package/build/Calendar/DayCalendar/DayCalendar.js.map +1 -1
- package/build/Calendar/DayCalendar/DayCalendar.mjs +4 -0
- package/build/Calendar/DayCalendar/DayCalendar.mjs.map +1 -1
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.js +30 -18
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.js.map +1 -1
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.mjs +30 -18
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.mjs.map +1 -1
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.js +1 -1
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.js.map +1 -1
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.mjs +1 -1
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.mjs.map +1 -1
- package/build/Calendar/{tableLink → TableLink}/TableLink.js +23 -11
- package/build/Calendar/TableLink/TableLink.js.map +1 -0
- package/build/Calendar/{tableLink → TableLink}/TableLink.mjs +23 -11
- package/build/Calendar/TableLink/TableLink.mjs.map +1 -0
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.js +1 -1
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.js.map +1 -1
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.mjs +1 -1
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.mjs.map +1 -1
- package/build/DateLookup/DateLookup.js +14 -4
- package/build/DateLookup/DateLookup.js.map +1 -1
- package/build/DateLookup/DateLookup.mjs +14 -4
- package/build/DateLookup/DateLookup.mjs.map +1 -1
- package/build/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.js +33 -0
- package/build/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.js.map +1 -0
- package/build/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.mjs +31 -0
- package/build/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.mjs.map +1 -0
- package/build/common/DateUtils/isDateDisabled/isDateDisabled.js +40 -0
- package/build/common/DateUtils/isDateDisabled/isDateDisabled.js.map +1 -0
- package/build/common/DateUtils/isDateDisabled/isDateDisabled.mjs +38 -0
- package/build/common/DateUtils/isDateDisabled/isDateDisabled.mjs.map +1 -0
- package/build/i18n/en.json +1 -0
- package/build/i18n/en.json.js +1 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +1 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/index.js +2 -4
- package/build/index.js.map +1 -1
- package/build/index.mjs +1 -2
- package/build/index.mjs.map +1 -1
- package/build/main.css +114 -76
- package/build/styles/Calendar/Calendar.css +104 -35
- package/build/styles/Calendar/TableLink/TableLink.css +109 -0
- package/build/styles/main.css +114 -76
- package/build/types/Body/Body.d.ts +2 -2
- package/build/types/Calendar/Calendar.d.ts +4 -2
- package/build/types/Calendar/Calendar.d.ts.map +1 -1
- package/build/types/Calendar/Calendar.messages.d.ts +5 -0
- package/build/types/Calendar/Calendar.messages.d.ts.map +1 -1
- package/build/types/Calendar/Calendar.types.d.ts +19 -0
- package/build/types/Calendar/Calendar.types.d.ts.map +1 -0
- package/build/types/Calendar/DayCalendar/DayCalendar.d.ts +4 -1
- package/build/types/Calendar/DayCalendar/DayCalendar.d.ts.map +1 -1
- package/build/types/Calendar/DayCalendar/Table/DayCalendarTable.d.ts +3 -0
- package/build/types/Calendar/DayCalendar/Table/DayCalendarTable.d.ts.map +1 -1
- package/build/types/Calendar/{tableLink → TableLink}/TableLink.d.ts +2 -1
- package/build/types/Calendar/TableLink/TableLink.d.ts.map +1 -0
- package/build/types/Calendar/TableLink/index.d.ts.map +1 -0
- package/build/types/Calendar/index.d.ts +1 -0
- package/build/types/Calendar/index.d.ts.map +1 -1
- package/build/types/DateLookup/DateLookup.d.ts +5 -2
- package/build/types/DateLookup/DateLookup.d.ts.map +1 -1
- package/build/types/DateLookup/index.d.ts +1 -0
- package/build/types/DateLookup/index.d.ts.map +1 -1
- package/build/types/IconButton/IconButton.d.ts +1 -1
- package/build/types/Title/Title.d.ts +2 -2
- package/build/types/Upload/Steps/UploadImageStep/UploadImageStep.d.ts +1 -1
- package/build/types/UploadInput/UploadItem/UploadItemLink.d.ts +1 -1
- package/build/types/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.d.ts +3 -0
- package/build/types/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.d.ts.map +1 -0
- package/build/types/common/DateUtils/index.d.ts +2 -0
- package/build/types/common/DateUtils/index.d.ts.map +1 -1
- package/build/types/common/DateUtils/isDateDisabled/isDateDisabled.d.ts +3 -0
- package/build/types/common/DateUtils/isDateDisabled/isDateDisabled.d.ts.map +1 -0
- package/build/types/index.d.ts +2 -4
- package/build/types/index.d.ts.map +1 -1
- package/build/types/test-utils/index.d.ts +2 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/Calendar/Calendar.css +104 -35
- package/src/Calendar/Calendar.disabledDates.test.tsx +197 -0
- package/src/Calendar/Calendar.less +5 -35
- package/src/Calendar/Calendar.messages.ts +5 -0
- package/src/Calendar/Calendar.test.tsx +9 -9
- package/src/Calendar/Calendar.tsx +61 -9
- package/src/Calendar/Calendar.types.ts +22 -0
- package/src/Calendar/DayCalendar/DayCalendar.tsx +17 -1
- package/src/Calendar/DayCalendar/Table/DayCalendarTable.tsx +42 -26
- package/src/Calendar/MonthCalendar/Table/MonthCalendarTable.tsx +1 -1
- package/src/Calendar/TableLink/TableLink.css +109 -0
- package/src/Calendar/TableLink/TableLink.less +60 -0
- package/src/Calendar/{tableLink → TableLink}/TableLink.tsx +24 -14
- package/src/Calendar/YearCalendar/Table/YearCalendarTable.tsx +1 -1
- package/src/Calendar/_stories/Calendar.accessibility.docs.mdx +112 -0
- package/src/Calendar/_stories/Calendar.story.tsx +55 -1
- package/src/Calendar/index.ts +7 -0
- package/src/DateLookup/DateLookup.disabledDates.test.tsx +120 -0
- package/src/DateLookup/DateLookup.test.tsx +1 -1
- package/src/DateLookup/DateLookup.tsx +11 -4
- package/src/DateLookup/_stories/DateLookup.accessibility.docs.mdx +100 -0
- package/src/DateLookup/_stories/DateLookup.story.tsx +59 -1
- package/src/DateLookup/index.ts +6 -0
- package/src/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.test.ts +98 -0
- package/src/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.ts +45 -0
- package/src/common/DateUtils/index.ts +2 -0
- package/src/common/DateUtils/isDateDisabled/isDateDisabled.test.ts +159 -0
- package/src/common/DateUtils/isDateDisabled/isDateDisabled.ts +59 -0
- package/src/i18n/en.json +1 -0
- package/src/index.ts +2 -4
- package/src/main.css +114 -76
- package/src/main.less +0 -1
- package/build/Calendar/tableLink/TableLink.js.map +0 -1
- package/build/Calendar/tableLink/TableLink.mjs.map +0 -1
- package/build/Markup/Markup.js +0 -136
- package/build/Markup/Markup.js.map +0 -1
- package/build/Markup/Markup.mjs +0 -131
- package/build/Markup/Markup.mjs.map +0 -1
- package/build/Markup/constants.js +0 -36
- package/build/Markup/constants.js.map +0 -1
- package/build/Markup/constants.mjs +0 -29
- package/build/Markup/constants.mjs.map +0 -1
- package/build/Markup/utils/parseMarkup.js +0 -226
- package/build/Markup/utils/parseMarkup.js.map +0 -1
- package/build/Markup/utils/parseMarkup.mjs +0 -223
- package/build/Markup/utils/parseMarkup.mjs.map +0 -1
- package/build/Markup/utils/sanitise.js +0 -91
- package/build/Markup/utils/sanitise.js.map +0 -1
- package/build/Markup/utils/sanitise.mjs +0 -89
- package/build/Markup/utils/sanitise.mjs.map +0 -1
- package/build/styles/Markup/Markup.css +0 -28
- package/build/styles/Markup/_stories/Vulnerability/Vulnerability.css +0 -70
- package/build/types/Calendar/tableLink/TableLink.d.ts.map +0 -1
- package/build/types/Calendar/tableLink/index.d.ts.map +0 -1
- package/build/types/Markup/Markup.d.ts +0 -18
- package/build/types/Markup/Markup.d.ts.map +0 -1
- package/build/types/Markup/Markup.types.d.ts +0 -68
- package/build/types/Markup/Markup.types.d.ts.map +0 -1
- package/build/types/Markup/_stories/Vulnerability/Vulnerability.d.ts +0 -12
- package/build/types/Markup/_stories/Vulnerability/Vulnerability.d.ts.map +0 -1
- package/build/types/Markup/constants.d.ts +0 -20
- package/build/types/Markup/constants.d.ts.map +0 -1
- package/build/types/Markup/index.d.ts +0 -3
- package/build/types/Markup/index.d.ts.map +0 -1
- package/build/types/Markup/utils/parseMarkup.d.ts +0 -6
- package/build/types/Markup/utils/parseMarkup.d.ts.map +0 -1
- package/build/types/Markup/utils/sanitise.d.ts +0 -2
- package/build/types/Markup/utils/sanitise.d.ts.map +0 -1
- package/src/Markup/Markup.css +0 -28
- package/src/Markup/Markup.injection.test.tsx +0 -152
- package/src/Markup/Markup.less +0 -29
- package/src/Markup/Markup.test.tsx +0 -405
- package/src/Markup/Markup.tsx +0 -165
- package/src/Markup/Markup.types.ts +0 -53
- package/src/Markup/_stories/Markup.docs.mdx +0 -75
- package/src/Markup/_stories/Markup.security.docs.mdx +0 -256
- package/src/Markup/_stories/Markup.story.tsx +0 -227
- package/src/Markup/_stories/Vulnerability/Vulnerability.css +0 -70
- package/src/Markup/_stories/Vulnerability/Vulnerability.tsx +0 -30
- package/src/Markup/constants.ts +0 -41
- package/src/Markup/index.ts +0 -2
- package/src/Markup/utils/parseMarkup.test.ts +0 -658
- package/src/Markup/utils/parseMarkup.ts +0 -228
- package/src/Markup/utils/sanitise.test.ts +0 -375
- package/src/Markup/utils/sanitise.ts +0 -107
- /package/build/types/Calendar/{tableLink → TableLink}/index.d.ts +0 -0
- /package/src/Calendar/{tableLink → TableLink}/index.ts +0 -0
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import './Vulnerability.css';
|
|
2
|
-
type Severity = 'High' | 'Medium' | 'Low';
|
|
3
|
-
interface VulnerabilityProps {
|
|
4
|
-
markup: string;
|
|
5
|
-
}
|
|
6
|
-
export declare const Vulnerability: ({ markup }: VulnerabilityProps) => import("react").JSX.Element;
|
|
7
|
-
interface BadgeProps {
|
|
8
|
-
severity: Severity;
|
|
9
|
-
}
|
|
10
|
-
export declare const Badge: ({ severity }: BadgeProps) => import("react").JSX.Element;
|
|
11
|
-
export {};
|
|
12
|
-
//# sourceMappingURL=Vulnerability.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Vulnerability.d.ts","sourceRoot":"","sources":["../../../../../src/Markup/_stories/Vulnerability/Vulnerability.tsx"],"names":[],"mappings":"AAEA,OAAO,qBAAqB,CAAC;AAE7B,KAAK,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE1C,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,aAAa,GAAI,YAAY,kBAAkB,gCAS3D,CAAC;AAEF,UAAU,UAAU;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,eAAO,MAAM,KAAK,GAAI,cAAc,UAAU,gCAI7C,CAAC"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export declare const ALLOWED_TAGS: readonly ["important", "positive", "negative", "strong", "strikethrough", "paragraph", "p", "link", "a"];
|
|
2
|
-
/**
|
|
3
|
-
* Matches opening and closing tags from the ALLOWED_TAGS allow-list.
|
|
4
|
-
*
|
|
5
|
-
* Capture groups:
|
|
6
|
-
* 1. `/` if this is a closing tag, undefined otherwise
|
|
7
|
-
* 2. Tag name
|
|
8
|
-
* 3. Raw attribute string between the tag name and `>` (empty for closing tags)
|
|
9
|
-
*
|
|
10
|
-
* The lookahead `(?=[\s>/])` after the tag name ensures we don't match partial
|
|
11
|
-
* words — e.g. `<stronger>` won't be matched as `<strong>`.
|
|
12
|
-
*
|
|
13
|
-
* Global flag (`g`) allows iterative matching via `exec()`.
|
|
14
|
-
*/
|
|
15
|
-
export declare const TAG_REGEX: RegExp;
|
|
16
|
-
export declare const TAG_ALIASES: Record<string, string>;
|
|
17
|
-
export declare const MAX_NESTING_DEPTH = 32;
|
|
18
|
-
export declare const ALLOWED_TARGETS: string[];
|
|
19
|
-
export declare const ALLOWED_PROTOCOLS: string[];
|
|
20
|
-
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/Markup/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,YAAY,0GAUf,CAAC;AAEX;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,SAAS,QAGrB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAiC,CAAC;AAGjF,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAGpC,eAAO,MAAM,eAAe,UAAsB,CAAC;AAGnD,eAAO,MAAM,iBAAiB,UAAwB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/Markup/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { MAX_NESTING_DEPTH } from '../constants';
|
|
2
|
-
import { MarkupNode } from '../Markup.types';
|
|
3
|
-
export { MAX_NESTING_DEPTH };
|
|
4
|
-
/** Entry point: tokenises the input string, then parses the token stream into a MarkupNode tree. */
|
|
5
|
-
export declare function parseMarkup(input: string): MarkupNode[];
|
|
6
|
-
//# sourceMappingURL=parseMarkup.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parseMarkup.d.ts","sourceRoot":"","sources":["../../../../src/Markup/utils/parseMarkup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,iBAAiB,EAA0B,MAAM,cAAc,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAuN7B,oGAAoG;AACpG,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,CAQvD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sanitise.d.ts","sourceRoot":"","sources":["../../../../src/Markup/utils/sanitise.ts"],"names":[],"mappings":"AAaA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAmFnD"}
|
package/src/Markup/Markup.css
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
.wds-markup-emphasis {
|
|
2
|
-
font-weight: 600;
|
|
3
|
-
font-weight: var(--font-weight-semi-bold);
|
|
4
|
-
font-style: normal;
|
|
5
|
-
}
|
|
6
|
-
.wds-markup-emphasis--important {
|
|
7
|
-
color: #37517e;
|
|
8
|
-
color: var(--color-content-primary);
|
|
9
|
-
}
|
|
10
|
-
.wds-markup-emphasis--positive {
|
|
11
|
-
color: #008026;
|
|
12
|
-
color: var(--color-content-positive);
|
|
13
|
-
}
|
|
14
|
-
.wds-markup-emphasis--negative {
|
|
15
|
-
color: #cf2929;
|
|
16
|
-
color: var(--color-content-negative);
|
|
17
|
-
}
|
|
18
|
-
.wds-markup-strong {
|
|
19
|
-
font-weight: 600;
|
|
20
|
-
font-weight: var(--font-weight-semi-bold);
|
|
21
|
-
}
|
|
22
|
-
.wds-markup-strikethrough {
|
|
23
|
-
-webkit-text-decoration: line-through;
|
|
24
|
-
text-decoration: line-through;
|
|
25
|
-
}
|
|
26
|
-
.wds-markup-paragraph {
|
|
27
|
-
margin: 0;
|
|
28
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
import { render } from '../test-utils';
|
|
4
|
-
|
|
5
|
-
import { Markup } from './Markup';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Proof of Concept: Markup Injection via Attacker-Controlled Data
|
|
9
|
-
*
|
|
10
|
-
* These tests demonstrate Laszlo's concern: when untrusted data (e.g. a payee name,
|
|
11
|
-
* company name, or any string from an external source) is interpolated into a markup
|
|
12
|
-
* template string, the attacker can break out of their structural context and inject
|
|
13
|
-
* arbitrary allowed markup — links, emphasis, etc.
|
|
14
|
-
*
|
|
15
|
-
* This is NOT about XSS (script execution is blocked). It's about content spoofing
|
|
16
|
-
* and phishing within trusted UI.
|
|
17
|
-
*/
|
|
18
|
-
describe('Markup injection via attacker-controlled data', () => {
|
|
19
|
-
describe('the vulnerability: structural injection', () => {
|
|
20
|
-
it('attacker-controlled data can inject links into trusted UI', () => {
|
|
21
|
-
// Scenario: BFF returns a payee name that an attacker controls
|
|
22
|
-
// (e.g. they registered a company with a crafted name)
|
|
23
|
-
const attackerControlledName =
|
|
24
|
-
'Acme Corp</strong>' +
|
|
25
|
-
'<link href="https://evil.example.com">' +
|
|
26
|
-
'<important>ACTION REQUIRED: Verify your identity</important>' +
|
|
27
|
-
'</link>' +
|
|
28
|
-
'<strong>';
|
|
29
|
-
|
|
30
|
-
// The frontend interpolates it into a template — a typical BFF integration pattern
|
|
31
|
-
const template = `Transfer <strong>£35.00</strong> to <strong>${attackerControlledName}</strong>`;
|
|
32
|
-
|
|
33
|
-
const { container } = render(<Markup>{template}</Markup>);
|
|
34
|
-
|
|
35
|
-
// The injected link renders as a real, clickable anchor tag
|
|
36
|
-
const injectedLink = container.querySelector('a[href="https://evil.example.com"]');
|
|
37
|
-
expect(injectedLink).toBeInTheDocument();
|
|
38
|
-
|
|
39
|
-
// The injected <important> renders with styling that makes it look authoritative
|
|
40
|
-
const injectedEmphasis = container.querySelector('.wds-markup-emphasis--important');
|
|
41
|
-
expect(injectedEmphasis).toBeInTheDocument();
|
|
42
|
-
expect(injectedEmphasis).toHaveTextContent('ACTION REQUIRED: Verify your identity');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('attacker can spoof UI hierarchy with paragraph injection', () => {
|
|
46
|
-
// A user-entered "note" field that ends up in markup
|
|
47
|
-
const attackerNote =
|
|
48
|
-
'Thanks!</p>' +
|
|
49
|
-
'<p><important>Your account has been compromised. ' +
|
|
50
|
-
'Contact support at </important>' +
|
|
51
|
-
'<link href="https://phishing.example.com">this secure link</link></p>' +
|
|
52
|
-
'<p>';
|
|
53
|
-
|
|
54
|
-
const template = `<p>Note from sender: ${attackerNote}</p>`;
|
|
55
|
-
|
|
56
|
-
const { container } = render(<Markup>{template}</Markup>);
|
|
57
|
-
|
|
58
|
-
const phishingLink = container.querySelector('a[href="https://phishing.example.com"]');
|
|
59
|
-
expect(phishingLink).toBeInTheDocument();
|
|
60
|
-
expect(phishingLink).toHaveTextContent('this secure link');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('attacker can inject misleading accessibility labels', () => {
|
|
64
|
-
// Even with valid structure, injected content can mislead screen readers
|
|
65
|
-
const attackerValue =
|
|
66
|
-
'normal text</strong>' +
|
|
67
|
-
'<strong accessibilityLabel="Security alert: click to verify">' +
|
|
68
|
-
'<link href="https://evil.example.com">here</link>' +
|
|
69
|
-
'</strong>' +
|
|
70
|
-
'<strong>';
|
|
71
|
-
|
|
72
|
-
const template = `Your balance: <strong>${attackerValue}</strong>`;
|
|
73
|
-
|
|
74
|
-
const { container } = render(<Markup>{template}</Markup>);
|
|
75
|
-
|
|
76
|
-
const misleadingElement = container.querySelector(
|
|
77
|
-
'[aria-label="Security alert: click to verify"]',
|
|
78
|
-
);
|
|
79
|
-
expect(misleadingElement).toBeInTheDocument();
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('what IS blocked (XSS / script execution)', () => {
|
|
84
|
-
it('cannot inject arbitrary HTML elements', () => {
|
|
85
|
-
const xssAttempt = '<img src=x onerror=alert(1)>';
|
|
86
|
-
const template = `Hello <strong>${xssAttempt}</strong>`;
|
|
87
|
-
|
|
88
|
-
const { container } = render(<Markup>{template}</Markup>);
|
|
89
|
-
|
|
90
|
-
// <img> is not in the allow-list, so it's rendered as text
|
|
91
|
-
expect(container.querySelector('img')).not.toBeInTheDocument();
|
|
92
|
-
expect(container).toHaveTextContent(/<img src=x onerror=alert\(1\)>/);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('cannot inject script tags', () => {
|
|
96
|
-
const xssAttempt = '<script>alert("xss")</script>';
|
|
97
|
-
const template = `Hello <strong>${xssAttempt}</strong>`;
|
|
98
|
-
|
|
99
|
-
const { container } = render(<Markup>{template}</Markup>);
|
|
100
|
-
|
|
101
|
-
expect(container.querySelector('script')).not.toBeInTheDocument();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('blocks javascript: URLs in links', () => {
|
|
105
|
-
// Even if attacker injects a link, javascript: URLs are blocked
|
|
106
|
-
const attack = 'click</strong><link href="javascript:alert(1)">me</link><strong>';
|
|
107
|
-
const template = `<strong>${attack}</strong>`;
|
|
108
|
-
|
|
109
|
-
const { container } = render(<Markup>{template}</Markup>);
|
|
110
|
-
|
|
111
|
-
// The link renders but without the dangerous href (falls back to Fragment)
|
|
112
|
-
expect(container.querySelector('a[href*="javascript"]')).not.toBeInTheDocument();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe('the general nature of this problem', () => {
|
|
117
|
-
it('is identical to HTML injection via innerHTML', () => {
|
|
118
|
-
// This is the same class of bug — mixing code and data in a string
|
|
119
|
-
const attackerName = '<b>URGENT</b>';
|
|
120
|
-
const div = document.createElement('div');
|
|
121
|
-
div.innerHTML = `Hello ${attackerName}`;
|
|
122
|
-
|
|
123
|
-
// The attacker's data became structure
|
|
124
|
-
expect(div.querySelector('b')).not.toBeNull();
|
|
125
|
-
expect(div.querySelector('b')!).toHaveTextContent('URGENT');
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('react JSX prevents this by design', () => {
|
|
129
|
-
// In contrast, React treats variables as pure data — never as structure
|
|
130
|
-
const attackerName = '<strong>URGENT</strong>';
|
|
131
|
-
|
|
132
|
-
const { container } = render(<span>Hello {attackerName}</span>);
|
|
133
|
-
|
|
134
|
-
// React escapes the string — it renders as visible text, not as a <strong> tag
|
|
135
|
-
expect(container.querySelector('strong')).not.toBeInTheDocument();
|
|
136
|
-
expect(container).toHaveTextContent(/<strong>URGENT<\/strong>/);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('demonstrates why Markup reintroduces the problem React solved', () => {
|
|
140
|
-
// The same attacker string that React handles safely...
|
|
141
|
-
const attackerName = '<strong>URGENT: Call +44 123 456 789 NOW</strong>';
|
|
142
|
-
|
|
143
|
-
// ...becomes dangerous when passed through Markup's string parser
|
|
144
|
-
const { container } = render(<Markup>{`Hello ${attackerName}`}</Markup>);
|
|
145
|
-
|
|
146
|
-
// The attacker's data has been promoted to structure
|
|
147
|
-
const injected = container.querySelector('strong');
|
|
148
|
-
expect(injected).toBeInTheDocument();
|
|
149
|
-
expect(injected).toHaveTextContent('URGENT: Call +44 123 456 789 NOW');
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
});
|
package/src/Markup/Markup.less
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
.wds-markup-emphasis {
|
|
2
|
-
font-weight: var(--font-weight-semi-bold);
|
|
3
|
-
font-style: normal;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
.wds-markup-emphasis--important {
|
|
7
|
-
color: var(--color-content-primary);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
.wds-markup-emphasis--positive {
|
|
11
|
-
color: var(--color-content-positive);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.wds-markup-emphasis--negative {
|
|
15
|
-
color: var(--color-content-negative);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
.wds-markup-strong {
|
|
19
|
-
font-weight: var(--font-weight-semi-bold);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/// @see https://www.webaxe.org/strikethrough-html-accessibility/
|
|
23
|
-
.wds-markup-strikethrough {
|
|
24
|
-
text-decoration: line-through;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.wds-markup-paragraph {
|
|
28
|
-
margin: 0;
|
|
29
|
-
}
|