@autobusal/order-confirm 0.0.3 → 1.0.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/CHANGELOG.md +24 -0
- package/OrderConfirm.tsx +18 -17
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this package are documented here. This project follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/) and [Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [1.0.1] - 2026-07-19
|
|
7
|
+
|
|
8
|
+
### Security
|
|
9
|
+
|
|
10
|
+
- `OrderConfirm.tsx`: fixed an HTML-injection (XSS) vector. The `hash` segment
|
|
11
|
+
comes straight from the URL (`/orders/:type/:hash`) and was interpolated into
|
|
12
|
+
markup rendered via `dangerouslySetInnerHTML`. Added an `escapeHtml` helper
|
|
13
|
+
that encodes `& < > " '` and applied it to the hash before interpolation
|
|
14
|
+
(`<strong>${ escapeHtml(String(hash ?? '')) }</strong>`), so a crafted hash
|
|
15
|
+
(e.g. containing `<img onerror=...>`) can no longer inject markup.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- `OrderConfirm.tsx`: the "Manage order" action now routes based on auth state.
|
|
20
|
+
A logged-in user is sent to `/account/orders` (where the order is listed)
|
|
21
|
+
instead of the public `/viewtrip/:hash` lookup form that email-gates access to
|
|
22
|
+
their own order; guests still use the public `/viewtrip/:hash` lookup.
|
|
23
|
+
|
|
24
|
+
Authored by Ferjolt Ozuni. Consolidated from the magus and alvavel whitelabel patch sets into canonical @autobusal source (eliminates per-repo patch-package divergence).
|
package/OrderConfirm.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { TFunction } from 'i18next';
|
|
2
|
-
import {
|
|
3
|
-
import { useSearchParams, useParams, useNavigate } from 'react-router-dom';
|
|
2
|
+
import { useSearchParams, useParams, useNavigate, Navigate } from 'react-router-dom';
|
|
4
3
|
import { AiOutlineCheck } from 'react-icons/ai';
|
|
5
4
|
import { BiErrorAlt } from 'react-icons/bi';
|
|
6
5
|
import { IoReturnUpBackOutline } from 'react-icons/io5';
|
|
@@ -13,6 +12,15 @@ interface Props {
|
|
|
13
12
|
t: TFunction<'common'>
|
|
14
13
|
}
|
|
15
14
|
|
|
15
|
+
// hash comes straight from the URL (/orders/:type/:hash) and is interpolated into
|
|
16
|
+
// HTML that gets rendered via dangerouslySetInnerHTML below - escape it so a
|
|
17
|
+
// crafted hash segment (e.g. containing <img onerror=...>) can't inject markup.
|
|
18
|
+
const escapeHtml = (value: string): string => (
|
|
19
|
+
value.replace(/[&<>"']/g, char => ({
|
|
20
|
+
'&': '&', '<': '<', '>': '>', '"': '"', '\'': '''
|
|
21
|
+
}[char] as string))
|
|
22
|
+
);
|
|
23
|
+
|
|
16
24
|
const OrderConfirm = ({ t }: Props): (JSX.Element | null) => {
|
|
17
25
|
const [ searchParams ] = useSearchParams();
|
|
18
26
|
|
|
@@ -24,28 +32,21 @@ const OrderConfirm = ({ t }: Props): (JSX.Element | null) => {
|
|
|
24
32
|
|
|
25
33
|
const { data: UserData } = useUserStore();
|
|
26
34
|
|
|
27
|
-
const isAllowed = ['reserved', 'cancelled', 'purchased'].includes(
|
|
28
|
-
String(type)
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
if (!isAllowed) {
|
|
33
|
-
navigate('/');
|
|
34
|
-
}
|
|
35
|
-
}, []);
|
|
35
|
+
const isAllowed = ['reserved', 'cancelled', 'purchased'].includes(String(type));
|
|
36
36
|
|
|
37
37
|
if (!isAllowed) {
|
|
38
|
-
return
|
|
38
|
+
return <Navigate to="/" />;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const onManage = (): void => {
|
|
42
|
-
|
|
42
|
+
// a logged-in user shouldn't be sent to the public order-lookup form (which
|
|
43
|
+
// email-gates access to their OWN order) - send them into their account
|
|
44
|
+
// orders where the order is listed. Guests use the public lookup.
|
|
45
|
+
navigate(UserData ? '/account/orders' : `/viewtrip/${ hash }`);
|
|
43
46
|
};
|
|
44
47
|
|
|
45
48
|
const onBack = (): void => {
|
|
46
|
-
navigate(
|
|
47
|
-
UserData ? '/account' : '/'
|
|
48
|
-
);
|
|
49
|
+
navigate(UserData ? '/account' : '/');
|
|
49
50
|
};
|
|
50
51
|
|
|
51
52
|
return (
|
|
@@ -56,7 +57,7 @@ const OrderConfirm = ({ t }: Props): (JSX.Element | null) => {
|
|
|
56
57
|
</Title>
|
|
57
58
|
|
|
58
59
|
<div className="box" dangerouslySetInnerHTML={{
|
|
59
|
-
__html: t(`order_confirm.content.${ type}`, { order: `<strong>${ hash }</strong>` })
|
|
60
|
+
__html: t(`order_confirm.content.${ type}`, { order: `<strong>${ escapeHtml(String(hash ?? '')) }</strong>` })
|
|
60
61
|
}} />
|
|
61
62
|
|
|
62
63
|
<>
|