@transferwise/components 0.0.0-experimental-fa3aa98 → 0.0.0-experimental-f4a328f
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 +16 -94
- package/build/Calendar/Calendar.js.map +1 -1
- package/build/Calendar/Calendar.messages.js +0 -12
- package/build/Calendar/Calendar.messages.js.map +1 -1
- package/build/Calendar/Calendar.messages.mjs +0 -12
- package/build/Calendar/Calendar.messages.mjs.map +1 -1
- package/build/Calendar/Calendar.mjs +17 -95
- package/build/Calendar/Calendar.mjs.map +1 -1
- package/build/Calendar/DateHeader/DateHeader.js +1 -5
- package/build/Calendar/DateHeader/DateHeader.js.map +1 -1
- package/build/Calendar/DateHeader/DateHeader.mjs +1 -5
- package/build/Calendar/DateHeader/DateHeader.mjs.map +1 -1
- package/build/Calendar/DayCalendar/DayCalendar.js +0 -14
- package/build/Calendar/DayCalendar/DayCalendar.js.map +1 -1
- package/build/Calendar/DayCalendar/DayCalendar.mjs +0 -14
- package/build/Calendar/DayCalendar/DayCalendar.mjs.map +1 -1
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.js +19 -53
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.js.map +1 -1
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.mjs +19 -53
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.mjs.map +1 -1
- package/build/Calendar/MonthCalendar/MonthCalendar.js +0 -6
- package/build/Calendar/MonthCalendar/MonthCalendar.js.map +1 -1
- package/build/Calendar/MonthCalendar/MonthCalendar.mjs +0 -6
- package/build/Calendar/MonthCalendar/MonthCalendar.mjs.map +1 -1
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.js +3 -8
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.js.map +1 -1
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.mjs +3 -8
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.mjs.map +1 -1
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.js +3 -8
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.js.map +1 -1
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.mjs +3 -8
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.mjs.map +1 -1
- package/build/Calendar/YearCalendar/YearCalendar.js +0 -7
- package/build/Calendar/YearCalendar/YearCalendar.js.map +1 -1
- package/build/Calendar/YearCalendar/YearCalendar.mjs +0 -7
- package/build/Calendar/YearCalendar/YearCalendar.mjs.map +1 -1
- package/build/Calendar/{TableLink → tableLink}/TableLink.js +11 -26
- package/build/Calendar/tableLink/TableLink.js.map +1 -0
- package/build/Calendar/{TableLink → tableLink}/TableLink.mjs +11 -26
- package/build/Calendar/tableLink/TableLink.mjs.map +1 -0
- package/build/DateLookup/DateLookup.js +9 -20
- package/build/DateLookup/DateLookup.js.map +1 -1
- package/build/DateLookup/DateLookup.mjs +9 -20
- package/build/DateLookup/DateLookup.mjs.map +1 -1
- package/build/Markup/Markup.js +137 -0
- package/build/Markup/Markup.js.map +1 -0
- package/build/Markup/Markup.mjs +132 -0
- package/build/Markup/Markup.mjs.map +1 -0
- package/build/Markup/constants.js +36 -0
- package/build/Markup/constants.js.map +1 -0
- package/build/Markup/constants.mjs +29 -0
- package/build/Markup/constants.mjs.map +1 -0
- package/build/Markup/utils/parseMarkup.js +236 -0
- package/build/Markup/utils/parseMarkup.js.map +1 -0
- package/build/Markup/utils/parseMarkup.mjs +231 -0
- package/build/Markup/utils/parseMarkup.mjs.map +1 -0
- package/build/Markup/utils/sanitise.js +114 -0
- package/build/Markup/utils/sanitise.js.map +1 -0
- package/build/Markup/utils/sanitise.mjs +111 -0
- package/build/Markup/utils/sanitise.mjs.map +1 -0
- package/build/i18n/en.json +0 -4
- package/build/i18n/en.json.js +0 -4
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +0 -4
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/index.js +4 -2
- package/build/index.js.map +1 -1
- package/build/index.mjs +2 -1
- package/build/index.mjs.map +1 -1
- package/build/main.css +78 -123
- package/build/styles/Calendar/Calendar.css +37 -112
- package/build/styles/Markup/Markup.css +28 -0
- package/build/styles/Markup/_stories/Vulnerability/Vulnerability.css +69 -0
- package/build/styles/main.css +78 -123
- package/build/types/Body/Body.d.ts +2 -2
- package/build/types/Calendar/Calendar.d.ts +3 -16
- package/build/types/Calendar/Calendar.d.ts.map +1 -1
- package/build/types/Calendar/Calendar.messages.d.ts +0 -20
- package/build/types/Calendar/Calendar.messages.d.ts.map +1 -1
- package/build/types/Calendar/DateHeader/DateHeader.d.ts +0 -2
- package/build/types/Calendar/DateHeader/DateHeader.d.ts.map +1 -1
- package/build/types/Calendar/DayCalendar/DayCalendar.d.ts +1 -5
- package/build/types/Calendar/DayCalendar/DayCalendar.d.ts.map +1 -1
- package/build/types/Calendar/DayCalendar/Table/DayCalendarTable.d.ts +0 -4
- package/build/types/Calendar/DayCalendar/Table/DayCalendarTable.d.ts.map +1 -1
- package/build/types/Calendar/MonthCalendar/MonthCalendar.d.ts +0 -1
- package/build/types/Calendar/MonthCalendar/MonthCalendar.d.ts.map +1 -1
- package/build/types/Calendar/MonthCalendar/Table/MonthCalendarTable.d.ts +1 -2
- package/build/types/Calendar/MonthCalendar/Table/MonthCalendarTable.d.ts.map +1 -1
- package/build/types/Calendar/YearCalendar/Table/YearCalendarTable.d.ts +1 -2
- package/build/types/Calendar/YearCalendar/Table/YearCalendarTable.d.ts.map +1 -1
- package/build/types/Calendar/YearCalendar/YearCalendar.d.ts +0 -1
- package/build/types/Calendar/YearCalendar/YearCalendar.d.ts.map +1 -1
- package/build/types/Calendar/index.d.ts +0 -1
- package/build/types/Calendar/index.d.ts.map +1 -1
- package/build/types/Calendar/{TableLink → tableLink}/TableLink.d.ts +1 -3
- 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/DateLookup/DateLookup.d.ts +5 -24
- package/build/types/DateLookup/DateLookup.d.ts.map +1 -1
- package/build/types/DateLookup/index.d.ts +0 -1
- package/build/types/DateLookup/index.d.ts.map +1 -1
- package/build/types/IconButton/IconButton.d.ts +1 -1
- package/build/types/Markup/Markup.d.ts +18 -0
- package/build/types/Markup/Markup.d.ts.map +1 -0
- package/build/types/Markup/Markup.types.d.ts +77 -0
- package/build/types/Markup/Markup.types.d.ts.map +1 -0
- package/build/types/Markup/_stories/Vulnerability/Vulnerability.d.ts +12 -0
- package/build/types/Markup/_stories/Vulnerability/Vulnerability.d.ts.map +1 -0
- package/build/types/Markup/constants.d.ts +20 -0
- package/build/types/Markup/constants.d.ts.map +1 -0
- package/build/types/Markup/index.d.ts +3 -0
- package/build/types/Markup/index.d.ts.map +1 -0
- package/build/types/Markup/utils/parseMarkup.d.ts +8 -0
- package/build/types/Markup/utils/parseMarkup.d.ts.map +1 -0
- package/build/types/Markup/utils/sanitise.d.ts +4 -0
- package/build/types/Markup/utils/sanitise.d.ts.map +1 -0
- 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/index.d.ts +0 -2
- package/build/types/common/DateUtils/index.d.ts.map +1 -1
- package/build/types/index.d.ts +4 -3
- package/build/types/index.d.ts.map +1 -1
- package/build/types/test-utils/index.d.ts +0 -8
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Calendar/Calendar.css +37 -112
- package/src/Calendar/Calendar.less +35 -5
- package/src/Calendar/Calendar.messages.ts +0 -21
- package/src/Calendar/Calendar.test.tsx +27 -288
- package/src/Calendar/Calendar.tsx +19 -136
- package/src/Calendar/DateHeader/DateHeader.tsx +0 -6
- package/src/Calendar/DayCalendar/DayCalendar.tsx +1 -33
- package/src/Calendar/DayCalendar/Table/DayCalendarTable.tsx +28 -62
- package/src/Calendar/MonthCalendar/MonthCalendar.tsx +0 -8
- package/src/Calendar/MonthCalendar/Table/MonthCalendarTable.tsx +3 -15
- package/src/Calendar/YearCalendar/Table/YearCalendarTable.tsx +3 -15
- package/src/Calendar/YearCalendar/YearCalendar.tsx +0 -9
- package/src/Calendar/_stories/Calendar.story.tsx +4 -138
- package/src/Calendar/index.ts +0 -7
- package/src/Calendar/{TableLink → tableLink}/TableLink.tsx +14 -29
- package/src/DateLookup/DateLookup.test.tsx +37 -34
- package/src/DateLookup/DateLookup.tsx +13 -37
- package/src/DateLookup/_stories/DateLookup.story.tsx +2 -124
- package/src/DateLookup/index.ts +0 -6
- package/src/Markup/Markup.css +28 -0
- package/src/Markup/Markup.injection.test.tsx +163 -0
- package/src/Markup/Markup.less +29 -0
- package/src/Markup/Markup.test.tsx +463 -0
- package/src/Markup/Markup.tsx +169 -0
- package/src/Markup/Markup.types.ts +62 -0
- package/src/Markup/_stories/Markup.docs.mdx +75 -0
- package/src/Markup/_stories/Markup.security.docs.mdx +343 -0
- package/src/Markup/_stories/Markup.story.tsx +266 -0
- package/src/Markup/_stories/Vulnerability/Vulnerability.css +69 -0
- package/src/Markup/_stories/Vulnerability/Vulnerability.tsx +30 -0
- package/src/Markup/constants.ts +41 -0
- package/src/Markup/index.ts +2 -0
- package/src/Markup/utils/parseMarkup.test.ts +706 -0
- package/src/Markup/utils/parseMarkup.ts +234 -0
- package/src/Markup/utils/sanitise.test.ts +497 -0
- package/src/Markup/utils/sanitise.ts +133 -0
- package/src/common/DateUtils/index.ts +0 -2
- package/src/i18n/en.json +0 -4
- package/src/index.ts +4 -3
- package/src/main.css +78 -123
- package/src/main.less +1 -0
- package/build/Calendar/TableLink/TableLink.js.map +0 -1
- package/build/Calendar/TableLink/TableLink.mjs.map +0 -1
- package/build/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.js +0 -33
- package/build/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.js.map +0 -1
- package/build/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.mjs +0 -31
- package/build/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.mjs.map +0 -1
- package/build/common/DateUtils/isDateDisabled/isDateDisabled.js +0 -40
- package/build/common/DateUtils/isDateDisabled/isDateDisabled.js.map +0 -1
- package/build/common/DateUtils/isDateDisabled/isDateDisabled.mjs +0 -38
- package/build/common/DateUtils/isDateDisabled/isDateDisabled.mjs.map +0 -1
- package/build/styles/Calendar/TableLink/TableLink.css +0 -115
- package/build/types/Calendar/Calendar.types.d.ts +0 -32
- package/build/types/Calendar/Calendar.types.d.ts.map +0 -1
- 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/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.d.ts +0 -3
- package/build/types/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.d.ts.map +0 -1
- package/build/types/common/DateUtils/isDateDisabled/isDateDisabled.d.ts +0 -3
- package/build/types/common/DateUtils/isDateDisabled/isDateDisabled.d.ts.map +0 -1
- package/src/Calendar/Calendar.disabledDates.test.tsx +0 -518
- package/src/Calendar/Calendar.types.ts +0 -35
- package/src/Calendar/TableLink/TableLink.css +0 -115
- package/src/Calendar/TableLink/TableLink.less +0 -68
- package/src/Calendar/_stories/Calendar.accessibility.docs.mdx +0 -118
- package/src/Calendar/_stories/Calendar.test.story.tsx +0 -74
- package/src/DateLookup/DateLookup.disabledDates.test.tsx +0 -293
- package/src/DateLookup/_stories/DateLookup.accessibility.docs.mdx +0 -108
- package/src/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.test.ts +0 -98
- package/src/common/DateUtils/hasDisabledDatesInMonth/hasDisabledDatesInMonth.ts +0 -45
- package/src/common/DateUtils/isDateDisabled/isDateDisabled.test.ts +0 -169
- package/src/common/DateUtils/isDateDisabled/isDateDisabled.ts +0 -59
- /package/build/types/Calendar/{TableLink → tableLink}/index.d.ts +0 -0
- /package/src/Calendar/{TableLink → tableLink}/index.ts +0 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import { isAllowedHref, isHrefAllowedByMode } from './sanitise';
|
|
2
|
+
|
|
3
|
+
describe('isAllowedHref', () => {
|
|
4
|
+
describe('allowed protocols', () => {
|
|
5
|
+
it('allows https URLs', () => {
|
|
6
|
+
expect(isAllowedHref('https://example.com')).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('allows mailto URLs', () => {
|
|
10
|
+
expect(isAllowedHref('mailto:user@example.com')).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('blocks http URLs', () => {
|
|
14
|
+
expect(isAllowedHref('http://example.com')).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('blocks tel URLs', () => {
|
|
18
|
+
expect(isAllowedHref('tel:+1234567890')).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('allowed relative paths', () => {
|
|
23
|
+
it('allows root-relative paths', () => {
|
|
24
|
+
expect(isAllowedHref('/settings')).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('allows hash fragments', () => {
|
|
28
|
+
expect(isAllowedHref('#section')).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('blocked relative paths', () => {
|
|
33
|
+
it('blocks dot-relative paths', () => {
|
|
34
|
+
expect(isAllowedHref('./page')).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('blocks parent-relative paths', () => {
|
|
38
|
+
expect(isAllowedHref('../page')).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('blocks dot-relative paths with query parameters', () => {
|
|
42
|
+
expect(isAllowedHref('./page?time=10:30')).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('blocks bare relative paths with colons in segments', () => {
|
|
46
|
+
expect(isAllowedHref('page?key=value:other')).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('blocked protocols', () => {
|
|
51
|
+
it('blocks javascript: protocol', () => {
|
|
52
|
+
// eslint-disable-next-line no-script-url
|
|
53
|
+
expect(isAllowedHref('javascript:alert(1)')).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('blocks javascript: with mixed case', () => {
|
|
57
|
+
// eslint-disable-next-line no-script-url
|
|
58
|
+
expect(isAllowedHref('JavaScript:alert(1)')).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('blocks data: protocol', () => {
|
|
62
|
+
expect(isAllowedHref('data:text/html,<script>alert(1)</script>')).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('blocks vbscript: protocol', () => {
|
|
66
|
+
expect(isAllowedHref('vbscript:MsgBox("xss")')).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('blocks unknown protocols', () => {
|
|
70
|
+
expect(isAllowedHref('ftp://evil.com')).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('protocol-relative URLs', () => {
|
|
75
|
+
it('blocks protocol-relative URLs', () => {
|
|
76
|
+
expect(isAllowedHref('//evil.com/phishing')).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('blocks protocol-relative URLs with whitespace', () => {
|
|
80
|
+
expect(isAllowedHref(' //evil.com ')).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('edge cases', () => {
|
|
85
|
+
it('handles empty string', () => {
|
|
86
|
+
expect(isAllowedHref('')).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('handles whitespace-padded URLs', () => {
|
|
90
|
+
expect(isAllowedHref(' https://example.com ')).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('handles whitespace-padded dangerous URLs', () => {
|
|
94
|
+
expect(isAllowedHref(' javascript:alert(1) ')).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('control characters in scheme — XSS bypass via URL parser throw', () => {
|
|
99
|
+
it('blocks null byte mid-scheme (java\\x00script:)', () => {
|
|
100
|
+
expect(isAllowedHref('java\x00script:alert(1)')).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('blocks null byte between s and c (javas\\x00cript:)', () => {
|
|
104
|
+
expect(isAllowedHref('javas\x00cript:alert(1)')).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('blocks SOH prefix before javascript:', () => {
|
|
108
|
+
expect(isAllowedHref('\x01javascript:alert(1)')).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('blocks vertical tab prefix before javascript:', () => {
|
|
112
|
+
expect(isAllowedHref('\x0Bjavascript:alert(1)')).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('blocks form feed prefix before javascript:', () => {
|
|
116
|
+
expect(isAllowedHref('\x0Cjavascript:alert(1)')).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('blocks tab mid-scheme (java\\tscript:)', () => {
|
|
120
|
+
expect(isAllowedHref('java\tscript:alert(1)')).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('blocks CRLF mid-scheme (java\\r\\nscript:)', () => {
|
|
124
|
+
expect(isAllowedHref('java\r\nscript:alert(1)')).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('blocks LF mid-scheme (j\\navascript:)', () => {
|
|
128
|
+
expect(isAllowedHref('j\navascript:alert(1)')).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('zero-width and invisible character bypass', () => {
|
|
133
|
+
it('blocks zero-width space prefix before javascript:', () => {
|
|
134
|
+
expect(isAllowedHref('javascript:alert(1)')).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('blocks zero-width space only (invisible link)', () => {
|
|
138
|
+
expect(isAllowedHref('')).toBe(false);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('blocks BOM-only string (invisible link)', () => {
|
|
142
|
+
expect(isAllowedHref('')).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('blocks BOM prefix before javascript:', () => {
|
|
146
|
+
expect(isAllowedHref('javascript:alert(1)')).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('blocks zero-width non-joiner prefix before javascript:', () => {
|
|
150
|
+
expect(isAllowedHref('javascript:alert(1)')).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('blocks zero-width joiner prefix before javascript:', () => {
|
|
154
|
+
expect(isAllowedHref('javascript:alert(1)')).toBe(false);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('non-ASCII whitespace prefix bypass', () => {
|
|
159
|
+
it('blocks NBSP prefix before javascript:', () => {
|
|
160
|
+
expect(isAllowedHref(' javascript:alert(1)')).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('blocks line separator prefix before javascript:', () => {
|
|
164
|
+
expect(isAllowedHref('\u2028javascript:alert(1)')).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('blocks paragraph separator prefix before javascript:', () => {
|
|
168
|
+
expect(isAllowedHref('\u2029javascript:alert(1)')).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('blocks NBSP before protocol-relative URL', () => {
|
|
172
|
+
expect(isAllowedHref(' //evil.com')).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('blocks NBSP-only string', () => {
|
|
176
|
+
expect(isAllowedHref(' ')).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('backslash-based open redirect', () => {
|
|
181
|
+
it('blocks /\\evil.com (browsers normalize \\ to /)', () => {
|
|
182
|
+
expect(isAllowedHref('/\\evil.com')).toBe(false);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('blocks /\\\\evil.com (double backslash)', () => {
|
|
186
|
+
expect(isAllowedHref('/\\\\evil.com')).toBe(false);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('allows /path\\with\\backslash (not at position 1)', () => {
|
|
190
|
+
expect(isAllowedHref('/path\\with\\backslash')).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('URL-encoded dangerous schemes', () => {
|
|
195
|
+
it('blocks javascript with URL-encoded colon (%3A)', () => {
|
|
196
|
+
expect(isAllowedHref('javascript%3Aalert(1)')).toBe(false);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('blocks javascript with lowercase URL-encoded colon (%3a)', () => {
|
|
200
|
+
expect(isAllowedHref('javascript%3aalert(1)')).toBe(false);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('credential confusion / phishing via userinfo', () => {
|
|
205
|
+
it('blocks https URLs with misleading userinfo (good.com@evil.com)', () => {
|
|
206
|
+
expect(isAllowedHref('https://good.com@evil.com')).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('blocks https URLs with port-like credentials', () => {
|
|
210
|
+
expect(isAllowedHref('https://evil.com:443@good.com')).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('allows https URLs without userinfo', () => {
|
|
214
|
+
expect(isAllowedHref('https://example.com/path')).toBe(true);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('allows mailto with @ (expected in email addresses)', () => {
|
|
218
|
+
expect(isAllowedHref('mailto:user@example.com')).toBe(true);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe('degenerate HTTPS URLs', () => {
|
|
223
|
+
it('blocks https:evil.com (no slashes — ambiguous host)', () => {
|
|
224
|
+
expect(isAllowedHref('https:evil.com')).toBe(false);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('blocks https:/evil.com (single slash — ambiguous)', () => {
|
|
228
|
+
expect(isAllowedHref('https:/evil.com')).toBe(false);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('blocks https:// (no host)', () => {
|
|
232
|
+
expect(isAllowedHref('https://')).toBe(false);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('blocks https:/// (empty host)', () => {
|
|
236
|
+
expect(isAllowedHref('https:///')).toBe(false);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('internal network / SSRF via allowed HTTPS', () => {
|
|
241
|
+
it('blocks https://127.0.0.1 (localhost)', () => {
|
|
242
|
+
expect(isAllowedHref('https://127.0.0.1/')).toBe(false);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('blocks https://[::1] (IPv6 localhost)', () => {
|
|
246
|
+
expect(isAllowedHref('https://[::1]/')).toBe(false);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('blocks hex-encoded IP (0x7f000001)', () => {
|
|
250
|
+
expect(isAllowedHref('https://0x7f000001/')).toBe(false);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('blocks decimal IP (2130706433)', () => {
|
|
254
|
+
expect(isAllowedHref('https://2130706433/')).toBe(false);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('blocks octal IP (0177.0.0.1)', () => {
|
|
258
|
+
expect(isAllowedHref('https://0177.0.0.1/')).toBe(false);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('blocks AWS metadata endpoint', () => {
|
|
262
|
+
expect(isAllowedHref('https://169.254.169.254/')).toBe(false);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('blocks GCP metadata endpoint', () => {
|
|
266
|
+
expect(isAllowedHref('https://metadata.google.internal/')).toBe(false);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('blocks localhost with trailing dot', () => {
|
|
270
|
+
expect(isAllowedHref('https://localhost./')).toBe(false);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('blocks .internal host with trailing dot', () => {
|
|
274
|
+
expect(isAllowedHref('https://metadata.google.internal./')).toBe(false);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('blocks fully expanded IPv6 loopback', () => {
|
|
278
|
+
expect(isAllowedHref('https://[0:0:0:0:0:0:0:1]/')).toBe(false);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe('XSS payloads in fragment and path', () => {
|
|
283
|
+
it('blocks XSS payload in hash fragment', () => {
|
|
284
|
+
expect(isAllowedHref('#<img onerror=alert(1)>')).toBe(false);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('blocks XSS payload in path', () => {
|
|
288
|
+
expect(isAllowedHref('/<img onerror=alert(1)>')).toBe(false);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('blocks attribute injection in hash fragment', () => {
|
|
292
|
+
expect(isAllowedHref('#" onclick="alert(1)')).toBe(false);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('allows normal hash fragments', () => {
|
|
296
|
+
expect(isAllowedHref('#section-1')).toBe(true);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('allows normal root-relative paths', () => {
|
|
300
|
+
expect(isAllowedHref('/normal/path?query=value')).toBe(true);
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe('UNC paths (Windows network shares)', () => {
|
|
305
|
+
it('blocks UNC path to external server', () => {
|
|
306
|
+
expect(isAllowedHref('\\\\evil.com\\share')).toBe(false);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
describe('space before colon in scheme', () => {
|
|
311
|
+
it('blocks "javascript :alert(1)" (space before colon)', () => {
|
|
312
|
+
expect(isAllowedHref('javascript :alert(1)')).toBe(false);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
describe('mailto abuse', () => {
|
|
317
|
+
it('blocks mailto with XSS in body parameter', () => {
|
|
318
|
+
expect(isAllowedHref('mailto:evil@evil.com?body=<script>alert(1)</script>')).toBe(false);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('blocks mailto with header injection (cc/bcc)', () => {
|
|
322
|
+
expect(isAllowedHref('mailto:evil@evil.com?cc=victim@good.com&bcc=spy@evil.com')).toBe(false);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('blocks mailto with no recipient (social engineering)', () => {
|
|
326
|
+
expect(isAllowedHref('mailto:?subject=urgent&body=click%20here')).toBe(false);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('allows simple mailto with just an address', () => {
|
|
330
|
+
expect(isAllowedHref('mailto:support@example.com')).toBe(true);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
describe('additional blocked protocols (regression)', () => {
|
|
335
|
+
it('blocks file: protocol', () => {
|
|
336
|
+
expect(isAllowedHref('file:///etc/passwd')).toBe(false);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('blocks ws: protocol', () => {
|
|
340
|
+
expect(isAllowedHref('ws://evil.com')).toBe(false);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('blocks wss: protocol', () => {
|
|
344
|
+
expect(isAllowedHref('wss://evil.com')).toBe(false);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('blocks blob: protocol', () => {
|
|
348
|
+
expect(isAllowedHref('blob:https://evil.com/uuid')).toBe(false);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('blocks data: with SVG XSS', () => {
|
|
352
|
+
expect(isAllowedHref('data:image/svg+xml,<svg onload=alert(1)>')).toBe(false);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('blocks data: with base64-encoded script', () => {
|
|
356
|
+
expect(isAllowedHref('data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==')).toBe(
|
|
357
|
+
false,
|
|
358
|
+
);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('blocks view-source: protocol', () => {
|
|
362
|
+
expect(isAllowedHref('view-source:https://evil.com')).toBe(false);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('blocks custom protocol (foo:bar)', () => {
|
|
366
|
+
expect(isAllowedHref('foo:bar')).toBe(false);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('blocks minimal protocol pattern (a:b)', () => {
|
|
370
|
+
expect(isAllowedHref('a:b')).toBe(false);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('blocks customprotocol:payload', () => {
|
|
374
|
+
expect(isAllowedHref('customprotocol:payload')).toBe(false);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
describe('whitespace-only inputs', () => {
|
|
379
|
+
it('blocks spaces only', () => {
|
|
380
|
+
expect(isAllowedHref(' ')).toBe(false);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('blocks tab/newline/CR only', () => {
|
|
384
|
+
expect(isAllowedHref('\t\n\r')).toBe(false);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
describe('isHrefAllowedByMode', () => {
|
|
390
|
+
describe('internal mode', () => {
|
|
391
|
+
it('allows root-relative paths', () => {
|
|
392
|
+
expect(isHrefAllowedByMode('/accounts', 'internal')).toBe(true);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('allows deep root-relative paths', () => {
|
|
396
|
+
expect(isHrefAllowedByMode('/accounts/123/details', 'internal')).toBe(true);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('allows wise.com URLs', () => {
|
|
400
|
+
expect(isHrefAllowedByMode('https://wise.com/help', 'internal')).toBe(true);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('allows wise.com deep paths', () => {
|
|
404
|
+
expect(isHrefAllowedByMode('https://wise.com/gb/send-money', 'internal')).toBe(true);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('blocks external domains', () => {
|
|
408
|
+
expect(isHrefAllowedByMode('https://example.com', 'internal')).toBe(false);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('blocks domains with wise.com as substring', () => {
|
|
412
|
+
expect(isHrefAllowedByMode('https://wise.company.com', 'internal')).toBe(false);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('blocks hash fragments', () => {
|
|
416
|
+
expect(isHrefAllowedByMode('#section', 'internal')).toBe(false);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('blocks mailto links', () => {
|
|
420
|
+
expect(isHrefAllowedByMode('mailto:user@wise.com', 'internal')).toBe(false);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
describe('URL-parser normalization bypass prevention', () => {
|
|
424
|
+
it('blocks /\\evil.com (backslash normalised to slash by browsers)', () => {
|
|
425
|
+
expect(isHrefAllowedByMode('/\\evil.com', 'internal')).toBe(false);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('blocks /\\\\evil.com (double backslash)', () => {
|
|
429
|
+
expect(isHrefAllowedByMode('/\\\\evil.com', 'internal')).toBe(false);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('allows /%2f/evil.com (percent-encoded slash stays as path segment)', () => {
|
|
433
|
+
expect(isHrefAllowedByMode('/%2f/evil.com', 'internal')).toBe(true);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('allows normal path with encoded characters', () => {
|
|
437
|
+
expect(isHrefAllowedByMode('/path/%E2%9C%93/done', 'internal')).toBe(true);
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('blocks path that resolves to a different origin via URL parser', () => {
|
|
441
|
+
expect(isHrefAllowedByMode('/\\@evil.com', 'internal')).toBe(false);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('allows root path (/)', () => {
|
|
445
|
+
expect(isHrefAllowedByMode('/', 'internal')).toBe(true);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('allows paths with query parameters', () => {
|
|
449
|
+
expect(isHrefAllowedByMode('/search?q=hello', 'internal')).toBe(true);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('allows paths with hash fragments', () => {
|
|
453
|
+
expect(isHrefAllowedByMode('/page#section', 'internal')).toBe(true);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
describe('all mode', () => {
|
|
459
|
+
it('allows root-relative paths', () => {
|
|
460
|
+
expect(isHrefAllowedByMode('/settings', 'all')).toBe(true);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('allows external domains', () => {
|
|
464
|
+
expect(isHrefAllowedByMode('https://example.com', 'all')).toBe(true);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('allows wise.com URLs', () => {
|
|
468
|
+
expect(isHrefAllowedByMode('https://wise.com/help', 'all')).toBe(true);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('allows hash fragments', () => {
|
|
472
|
+
expect(isHrefAllowedByMode('#section', 'all')).toBe(true);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('allows mailto links', () => {
|
|
476
|
+
expect(isHrefAllowedByMode('mailto:user@example.com', 'all')).toBe(true);
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
describe('none mode', () => {
|
|
481
|
+
it('blocks root-relative paths', () => {
|
|
482
|
+
expect(isHrefAllowedByMode('/accounts', 'none')).toBe(false);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it('blocks wise.com URLs', () => {
|
|
486
|
+
expect(isHrefAllowedByMode('https://wise.com/help', 'none')).toBe(false);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('blocks external domains', () => {
|
|
490
|
+
expect(isHrefAllowedByMode('https://example.com', 'none')).toBe(false);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('blocks hash fragments', () => {
|
|
494
|
+
expect(isHrefAllowedByMode('#section', 'none')).toBe(false);
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { ALLOWED_PROTOCOLS } from '../constants';
|
|
2
|
+
|
|
3
|
+
const INVISIBLE_CHARS = /[\u200b\u200c\u200d\ufeff\u00ad\u2060\u180e]/g;
|
|
4
|
+
// eslint-disable-next-line regexp/no-control-character
|
|
5
|
+
const CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f]/;
|
|
6
|
+
const DANGEROUS_HTML_CHARS = /[<>"']/;
|
|
7
|
+
const DANGEROUS_SCHEME = /^(?:javascript|vbscript|data|jscript)\s*:/i;
|
|
8
|
+
// eslint-disable-next-line regexp/no-useless-non-capturing-group
|
|
9
|
+
const ENCODED_COLON_SCHEME = /^(?:javascript|vbscript|data|jscript)(?:%3a)/i;
|
|
10
|
+
const IPV6_BRACKETS = /^\[|\]$/g;
|
|
11
|
+
const NUMERIC_IP = /^[\d.]+$/;
|
|
12
|
+
const HEX_IP = /^0x[0-9a-f]+$/i;
|
|
13
|
+
|
|
14
|
+
export function isAllowedHref(href: string): boolean {
|
|
15
|
+
const cleaned = href.replace(INVISIBLE_CHARS, '');
|
|
16
|
+
const trimmed = cleaned.trim();
|
|
17
|
+
|
|
18
|
+
if (!trimmed) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (CONTROL_CHARS.test(trimmed)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (DANGEROUS_HTML_CHARS.test(trimmed)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (DANGEROUS_SCHEME.test(trimmed)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (ENCODED_COLON_SCHEME.test(trimmed)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (trimmed.startsWith('//')) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (trimmed.startsWith('\\')) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (trimmed.startsWith('/')) {
|
|
47
|
+
return trimmed[1] !== '\\';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (trimmed.startsWith('#')) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (trimmed.startsWith('./') || trimmed.startsWith('../')) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const url = new URL(trimmed);
|
|
60
|
+
|
|
61
|
+
if (!ALLOWED_PROTOCOLS.includes(url.protocol)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (url.protocol === 'https:') {
|
|
66
|
+
if (!trimmed.startsWith('https://')) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!url.hostname) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (url.username || url.password) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (isInternalHost(url.hostname)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (url.protocol === 'mailto:') {
|
|
84
|
+
if (url.search) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!url.pathname) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return true;
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type AllowLinksMode = 'internal' | 'all' | 'none';
|
|
100
|
+
|
|
101
|
+
// Synthetic origin used to resolve relative paths via the WHATWG URL parser.
|
|
102
|
+
// If the resolved origin still equals this sentinel, the path stays on-origin.
|
|
103
|
+
const SSR_SENTINEL_ORIGIN = 'https://internal.invalid';
|
|
104
|
+
|
|
105
|
+
export function isHrefAllowedByMode(href: string, mode: AllowLinksMode): boolean {
|
|
106
|
+
switch (mode) {
|
|
107
|
+
case 'none':
|
|
108
|
+
return false;
|
|
109
|
+
case 'all':
|
|
110
|
+
return true;
|
|
111
|
+
case 'internal':
|
|
112
|
+
if (href.startsWith('https://wise.com/')) return true;
|
|
113
|
+
if (href.startsWith('/')) {
|
|
114
|
+
try {
|
|
115
|
+
return new URL(href, SSR_SENTINEL_ORIGIN).origin === SSR_SENTINEL_ORIGIN;
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isInternalHost(hostname: string): boolean {
|
|
125
|
+
const stripped = hostname.replace(IPV6_BRACKETS, '');
|
|
126
|
+
const host = stripped.endsWith('.') ? stripped.slice(0, -1) : stripped;
|
|
127
|
+
if (NUMERIC_IP.test(host)) return true;
|
|
128
|
+
if (HEX_IP.test(host)) return true;
|
|
129
|
+
if (host === '::1' || host === '0:0:0:0:0:0:0:1') return true;
|
|
130
|
+
if (host === 'localhost') return true;
|
|
131
|
+
if (host.endsWith('.internal')) return true;
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
@@ -6,5 +6,3 @@ export { isDateValid } from './isDateValid/isDateValid';
|
|
|
6
6
|
export { isMonthAndYearFormat } from './isMonthAndYearFormat/isMonthAndYearFormat';
|
|
7
7
|
export { MDY, YMD } from './getFormatForLocale/getFormatForLocale';
|
|
8
8
|
export { returnDateView } from './getDateView/getDateView';
|
|
9
|
-
export { isDateDisabled } from './isDateDisabled/isDateDisabled';
|
|
10
|
-
export { hasDisabledDatesInMonth } from './hasDisabledDatesInMonth/hasDisabledDatesInMonth';
|
package/src/i18n/en.json
CHANGED
|
@@ -12,10 +12,6 @@
|
|
|
12
12
|
"neptune.DateLookup.goTo20YearView": "Go to 20 year view",
|
|
13
13
|
"neptune.DateLookup.month": "month",
|
|
14
14
|
"neptune.DateLookup.next": "next",
|
|
15
|
-
"neptune.DateLookup.notAvailable": "not available",
|
|
16
|
-
"neptune.DateLookup.outOfRangeAfter": "Please select a date on or after {min}.",
|
|
17
|
-
"neptune.DateLookup.outOfRangeBefore": "Please select a date on or before {max}.",
|
|
18
|
-
"neptune.DateLookup.outOfRangeBetween": "Please select a date between {min} and {max}.",
|
|
19
15
|
"neptune.DateLookup.previous": "previous",
|
|
20
16
|
"neptune.DateLookup.selected": "selected",
|
|
21
17
|
"neptune.DateLookup.twentyYears": "20 years",
|
package/src/index.ts
CHANGED
|
@@ -25,8 +25,6 @@ export type {
|
|
|
25
25
|
ButtonProps as LegacyButtonProps,
|
|
26
26
|
AnchorElementProps,
|
|
27
27
|
} from './Button';
|
|
28
|
-
export type { CalendarProps } from './Calendar';
|
|
29
|
-
export type { DisabledRules, WeekDayType, DisabledDateRange } from './Calendar';
|
|
30
28
|
export type { CardProps } from './_deprecated/Card';
|
|
31
29
|
export type {
|
|
32
30
|
CarouselProps,
|
|
@@ -57,6 +55,7 @@ export type {
|
|
|
57
55
|
export type { BottomSheetProps } from './common/BottomSheet';
|
|
58
56
|
export type { DateInputProps } from './DateInput';
|
|
59
57
|
export type { DateLookupProps } from './DateLookup';
|
|
58
|
+
export type { CalendarProps } from './Calendar';
|
|
60
59
|
export type { DecisionProps } from './Decision';
|
|
61
60
|
export type {
|
|
62
61
|
DefinitionListProps,
|
|
@@ -92,6 +91,7 @@ export type { LabelProps, LabelOptionalProps, LabelDescriptionProps } from './La
|
|
|
92
91
|
export type { LegacyListItemProps } from './_deprecated/LegacyListItem';
|
|
93
92
|
export type { LoaderProps } from './Loader';
|
|
94
93
|
export type { MarkdownProps } from './Markdown';
|
|
94
|
+
export type { MarkupActionHandler, MarkupActions, MarkupNode, MarkupProps } from './Markup';
|
|
95
95
|
export type { ModalProps } from './Modal';
|
|
96
96
|
export type { MoneyProps } from './Money';
|
|
97
97
|
export type {
|
|
@@ -202,7 +202,6 @@ export { default as Badge, BadgeAssets } from './_deprecated/Badge';
|
|
|
202
202
|
export { default as Body } from './Body';
|
|
203
203
|
export { default as Button } from './Button';
|
|
204
204
|
export { default as IconButton } from './IconButton';
|
|
205
|
-
export { default as Calendar } from './Calendar';
|
|
206
205
|
export { default as Carousel } from './Carousel';
|
|
207
206
|
export { default as Card } from './_deprecated/Card';
|
|
208
207
|
export { default as Checkbox } from './Checkbox';
|
|
@@ -218,6 +217,7 @@ export { default as BaseCard } from './common/BaseCard';
|
|
|
218
217
|
export { default as CriticalCommsBanner } from './_deprecated/CriticalBanner';
|
|
219
218
|
export { default as DateInput } from './DateInput';
|
|
220
219
|
export { default as DateLookup } from './DateLookup';
|
|
220
|
+
export { default as Calendar } from './Calendar';
|
|
221
221
|
export { default as Decision } from './Decision';
|
|
222
222
|
export { default as DefinitionList } from './DefinitionList';
|
|
223
223
|
export { default as Dimmer } from './Dimmer';
|
|
@@ -250,6 +250,7 @@ export { default as LegacyListItem } from './_deprecated/LegacyListItem';
|
|
|
250
250
|
export { default as Loader } from './Loader';
|
|
251
251
|
export { default as Logo } from './Logo';
|
|
252
252
|
export { default as Markdown } from './Markdown';
|
|
253
|
+
export { default as Markup } from './Markup';
|
|
253
254
|
export { default as Modal } from './Modal';
|
|
254
255
|
export { default as Money } from './Money';
|
|
255
256
|
export { default as MoneyInput } from './MoneyInput';
|