Skip to content

Commit

Permalink
Merge pull request #91 from EyeSeeTea/fix/security-issues
Browse files Browse the repository at this point in the history
Security issues + i18n refactors
  • Loading branch information
adrianq authored Aug 14, 2024
2 parents d4f89fd + 6ecd18a commit 33fc8b4
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 893 deletions.
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
"@dhis2/d2-i18n-extract": "1.0.8",
"@dhis2/d2-i18n-generate": "1.2.0",
"@dhis2/ui": "6.12.0",
"@eyeseetea/d2-api": "1.14.0",
"@eyeseetea/d2-ui-components": "2.7.0",
"@eyeseetea/feedback-component": "0.0.3",
"@eyeseetea/d2-api": "1.16.0-beta.6",
"@eyeseetea/d2-ui-components": "2.9.0-beta.3",
"@eyeseetea/feedback-component": "0.1.3-beta.1",
"@material-ui/core": "4.12.4",
"@material-ui/icons": "4.11.3",
"@material-ui/lab": "4.0.0-alpha.60",
Expand All @@ -34,8 +34,8 @@
"react-dom": "^18.2.0",
"react-router-dom": "5.2.0",
"real-cancellable-promise": "^1.1.2",
"styled-components": "5.3.5",
"styled-jsx": "3.4.5",
"styled-components": "6.1.11",
"styled-jsx": "^5.1.6",
"typed-immutable-map": "^0.1.1",
"zustand": "^4.3.7"
},
Expand Down
4 changes: 2 additions & 2 deletions src/app-config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FeedbackOptions } from "@eyeseetea/feedback-component";

export const appConfig: AppConfig = {
appKey: "dhis2-app-skeleton",
id: "dhis2-app-skeleton",
appearance: {
showShareButton: true,
},
Expand All @@ -22,7 +22,7 @@ export const appConfig: AppConfig = {
};

export interface AppConfig {
appKey: string;
id: string;
appearance: {
showShareButton: boolean;
};
Expand Down
38 changes: 38 additions & 0 deletions src/utils/i18n-typed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// @ts-ignore
import i18n from "$/locales";

export function getModuleForNamespace(namespace: string) {
return {
t: function <Str extends string>(...args: I18nTArgs<Str>): string {
const [s, options] = args;
return i18n.t(s, { ...options, ns: namespace });
},
changeLanguage: i18n.changeLanguage.bind(i18n),
setDefaultNamespace: i18n.setDefaultNamespace.bind(i18n),
};
}

type I18nTArgs<Str extends string> = Interpolations<Str> extends Record<string, never>
? [Str] | [Str, Partial<Options>]
: [Str, Interpolations<Str> & Partial<Options>];

interface Options {
ns: string; // namespace
nsSeparator: string | boolean; // By default, ":", which breaks strings containing that char
lng: string; // language
}

type Interpolations<Str extends string> = Record<ExtractVars<Str>, string | number>;

type ExtractVars<Str extends string> = Str extends `${string}{{${infer Var}}}${infer StrRest}`
? Var | ExtractVars<StrRest>
: never;

/* Tests */

type IsEqual<T1, T2> = [T1] extends [T2] ? ([T2] extends [T1] ? true : false) : false;
const assertEqualTypes = <T1, T2>(_eq: IsEqual<T1, T2>): void => {};

assertEqualTypes<ExtractVars<"">, never>(true);
assertEqualTypes<ExtractVars<"name={{name}}">, "name">(true);
assertEqualTypes<ExtractVars<"name={{name}} age={{age}}">, "name" | "age">(true);
37 changes: 2 additions & 35 deletions src/utils/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,3 @@
// @ts-ignore
import i18n from "$/locales";
import { appConfig } from "$/app-config";
import { getModuleForNamespace } from "./i18n-typed";

function t<Str extends string>(s: Str, namespace?: GetNamespace<Str>): string {
return i18n.t(s, { ...namespace, ns: appConfig.appKey });
}

interface Options {
nsSeparator: string | boolean;
lng: string;
}

type GetNamespace<Str extends string> = Record<ExtractVars<Str>, string | number> &
Partial<Options>;

type ExtractVars<Str extends string> = Str extends `${string}{{${infer Var}}}${infer StrRest}`
? Var | ExtractVars<StrRest>
: never;

/* Tests */

type IsEqual<T1, T2> = [T1] extends [T2] ? ([T2] extends [T1] ? true : false) : false;
const assertEqualTypes = <T1, T2>(_eq: IsEqual<T1, T2>): void => {};

assertEqualTypes<ExtractVars<"">, never>(true);
assertEqualTypes<ExtractVars<"name={{name}}">, "name">(true);
assertEqualTypes<ExtractVars<"name={{name}} age={{age}}">, "name" | "age">(true);

const i18nTyped = {
t,
changeLanguage: i18n.changeLanguage.bind(i18n),
setDefaultNamespace: i18n.setDefaultNamespace.bind(i18n),
};

export default i18nTyped;
export default getModuleForNamespace("dhis2-skeleton-app");
9 changes: 8 additions & 1 deletion src/webapp/pages/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import styled from "styled-components";
import { HeaderBar } from "@dhis2/ui";
import { SnackbarProvider } from "@eyeseetea/d2-ui-components";
import { Feedback } from "@eyeseetea/feedback-component";
Expand Down Expand Up @@ -43,7 +44,7 @@ function App(props: AppProps) {
<MuiThemeProvider theme={muiTheme}>
<OldMuiThemeProvider muiTheme={muiThemeLegacy}>
<SnackbarProvider>
<HeaderBar appName="Skeleton App" />
<StyledHeaderBar appName="Skeleton App" />

{appConfig.feedback && appContext && (
<Feedback
Expand All @@ -65,4 +66,10 @@ function App(props: AppProps) {
);
}

const StyledHeaderBar = styled(HeaderBar)`
div:first-of-type {
box-sizing: border-box;
}
`;

export default React.memo(App);
50 changes: 19 additions & 31 deletions src/webapp/pages/app/__tests__/__snapshots__/App.spec.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,27 @@
exports[`App > navigates to page 1`] = `
<DocumentFragment>
<header
class="jsx-2837859311 "
class="jsx-2837859311 sc-dhKdPU klBWsW"
/>
<div
class="Component-root-24 Component-root-25"
class="Component-root-4 Component-root-5"
>
<button
class="MuiButtonBase-root MuiButton-root MuiButton-contained Component-root-26 Component-root-27 MuiButton-containedPrimary MuiButton-disableElevation"
tabindex="0"
type="button"
<div
style="padding: 2px 10px 2px 10px; font-size: 12px; cursor: pointer; color: rgb(255, 255, 255); background-color: rgb(255, 152, 0); font-family: \\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif; font-weight: 500; line-height: 1.75; letter-spacing: 0.02857em; text-transform: uppercase;"
>
<span
class="MuiButton-label"
Send feedback
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeSmall"
focusable="false"
style="padding-top: 2px; margin-left: 3px;"
viewBox="0 0 24 24"
>
Send feedback
<span
class="MuiButton-endIcon MuiButton-iconSizeMedium"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 12h-2v-2h2v2zm0-4h-2V6h2v4z"
/>
</svg>
</span>
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
<path
d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 12h-2v-2h2v2zm0-4h-2V6h2v4z"
/>
</svg>
</div>
</div>
<div
class="content"
Expand All @@ -44,7 +32,7 @@ exports[`App > navigates to page 1`] = `
<div>
<button
aria-label="Back"
class="MuiButtonBase-root MuiIconButton-root sc-gsnTZi sc-dkzDqf gEgEZ iDppS MuiIconButton-colorSecondary"
class="MuiButtonBase-root MuiIconButton-root sc-gEvDqW sc-eqUzNf hwoczq haqPoX MuiIconButton-colorSecondary"
data-test="page-header-back"
tabindex="0"
type="button"
Expand All @@ -64,14 +52,14 @@ exports[`App > navigates to page 1`] = `
/>
</button>
<h5
class="MuiTypography-root sc-bczRLJ WGVdy MuiTypography-h5 MuiTypography-gutterBottom"
class="MuiTypography-root sc-aYaIB hZQTlr MuiTypography-h5 MuiTypography-gutterBottom"
data-test="page-header-title"
>
Detail page
</h5>
</div>
<h2
class="sc-hKMtZM eeDtie"
class="sc-fqkwJk lcQFEV"
>
Hello John
</h2>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exports[`ExamplePage > renders the feedback component 1`] = `
<div>
<button
aria-label="Back"
class="MuiButtonBase-root MuiIconButton-root sc-gsnTZi sc-dkzDqf gEgEZ iDppS MuiIconButton-colorSecondary"
class="MuiButtonBase-root MuiIconButton-root sc-gEvDqW sc-eqUzNf hwoczq haqPoX MuiIconButton-colorSecondary"
data-test="page-header-back"
tabindex="0"
type="button"
Expand All @@ -25,14 +25,14 @@ exports[`ExamplePage > renders the feedback component 1`] = `
/>
</button>
<h5
class="MuiTypography-root sc-bczRLJ WGVdy MuiTypography-h5 MuiTypography-gutterBottom"
class="MuiTypography-root sc-aYaIB hZQTlr MuiTypography-h5 MuiTypography-gutterBottom"
data-test="page-header-title"
>
Detail page
</h5>
</div>
<h2
class="sc-hKMtZM eeDtie"
class="sc-fqkwJk lcQFEV"
>
Hello Mary
</h2>
Expand Down
8 changes: 7 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ export default ({ mode }) => {
nodePolyfills(),
react(),
checker({
overlay: false,
typescript: true,
// eslint: { lintCommand: 'eslint "./src/**/*.{ts,tsx}"', dev: { logLevel: ["error"] } },
eslint: {
lintCommand: 'eslint "./src/**/*.{ts,tsx}"',
dev: {
logLevel: ["warning"],
},
},
}),
],
test: {
Expand Down
Loading

0 comments on commit 33fc8b4

Please sign in to comment.