CVE-2022-23067: Insufficient Information (NVD-CWE-noinfo)
value: ToolJet versions v0.5.0 to v1.2.2 are vulnerable to token leakage via Referer header that leads to account takeover . If the user opens the invite link/signup link and then clicks on any external links within the page, it leaks the password set token/signup token in the referer header. Using these tokens the attacker can access the user’s account.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 8.8
Published: 2022-05-18T14:15Z
Repository: https://github.com/ToolJet/ToolJet
Language: Other
Files changed: frontend/src/App/App.jsx, frontend/src/ConfirmationPage/ConfirmationPage.jsx, frontend/src/ConfirmationPage/index.js
Commit date: 2022-03-03 20:16:35 +0530
Commit message: Fix token present in referrer header (#2413)


Diff: diff --git a/frontend/src/App/App.jsx b/frontend/src/App/App.jsx
index 415df937..578fbbe2 100644
--- a/frontend/src/App/App.jsx
+++ b/frontend/src/App/App.jsx
@@ -1,14 +1,14 @@
 import React from 'react';
-import { Router, Route } from 'react-router-dom';
+import { Router, Route, Redirect } from 'react-router-dom';
 import { history } from '@/_helpers';
 import { authenticationService, tooljetService } from '@/_services';
 import { PrivateRoute } from '@/_components';
 import { HomePage } from '@/HomePage';
 import { LoginPage } from '@/LoginPage';
 import { SignupPage } from '@/SignupPage';
-import { InvitationPage } from '@/InvitationPage';
+import { ConfirmationPage } from '@/ConfirmationPage';
 import { Authorize } from '@/Oauth2';
 import { Authorize as Oauth } from '@/Oauth';
 import { Editor, Viewer } from '@/Editor';
 import '@/_styles/theme.scss';
 import 'emoji-mart/css/emoji-mart.css';
@@ -116,11 +116,22 @@ class App extends React.Component {
             <Route path="/login" component={LoginPage} />
             <Route path="/sso/:origin" component={Oauth} />
             <Route path="/signup" component={SignupPage} />
             <Route path="/forgot-password" component={ForgotPassword} />
             <Route path="/reset-password" component={ResetPassword} />
-            <Route path="/invitations/:token" component={InvitationPage} />
+            <Route
+              path="/invitations/:token"
+              render={(props) => (
+                <Redirect
+                  to={{
+                    pathname: '/confirm',
+                    state: { token: props.match.params.token, search: props.location.search },
+                  }}
+                />
+              )}
+            />
+            <Route path="/confirm" component={ConfirmationPage} />
             <PrivateRoute
               exact
               path="/apps/:id"
               component={Editor}
               switchDarkMode={this.switchDarkMode}
diff --git a/frontend/src/InvitationPage/InvitationPage.jsx b/frontend/src/ConfirmationPage/ConfirmationPage.jsx
similarity index 97%
rename from frontend/src/InvitationPage/InvitationPage.jsx
rename to frontend/src/ConfirmationPage/ConfirmationPage.jsx
index 5bf0380c..92bb2cbb 100644
--- a/frontend/src/InvitationPage/InvitationPage.jsx
+++ b/frontend/src/ConfirmationPage/ConfirmationPage.jsx
@@ -1,17 +1,17 @@
 import React from 'react';
 import { userService } from '@/_services';
 import { toast } from 'react-hot-toast';
 import queryString from 'query-string';
 
-class InvitationPage extends React.Component {
+class ConfirmationPage extends React.Component {
   constructor(props) {
     super(props);
 
     this.state = {
       isLoading: false,
-      newSignup: queryString.parse(props.location.search).signup,
+      newSignup: queryString.parse(props.location.state.search).signup,
     };
     this.formRef = React.createRef(null);
   }
 
   handleChange = (event) => {
@@ -24,11 +24,11 @@ class InvitationPage extends React.Component {
   }
 
   setPassword = (e) => {
     e.preventDefault();
 
-    const token = this.props.match.params.token;
+    const token = this.props.location.state.token;
     const { password, organization, role, newSignup, firstName, lastName, password_confirmation } = this.state;
     this.setState({ isLoading: true });
 
     if (!password || !password_confirmation || !password.trim() || !password_confirmation.trim()) {
       this.setState({ isLoading: false });
@@ -201,6 +201,6 @@ class InvitationPage extends React.Component {
       </div>
     );
   }
 }
 
-export { InvitationPage };
+export { ConfirmationPage };
diff --git a/frontend/src/ConfirmationPage/index.js b/frontend/src/ConfirmationPage/index.js
new file mode 100644
index 00000000..095a93bc
--- /dev/null
+++ b/frontend/src/ConfirmationPage/index.js
@@ -0,0 +1 @@
+export * from './ConfirmationPage';
diff --git a/frontend/src/InvitationPage/index.js b/frontend/src/InvitationPage/index.js
deleted file mode 100644
index cdca7c9f..00000000
--- a/frontend/src/InvitationPage/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export * from './InvitationPage';

Vulnerable code: import { Router, Route } from 'react-router-dom';
import { InvitationPage } from '@/InvitationPage';
            <Route path="/invitations/:token" component={InvitationPage} />
class InvitationPage extends React.Component {
      newSignup: queryString.parse(props.location.search).signup,
    const token = this.props.match.params.token;
export { InvitationPage };
export * from './InvitationPage';
Fixed code: import { Router, Route, Redirect } from 'react-router-dom';
import { ConfirmationPage } from '@/ConfirmationPage';
            <Route
              path="/invitations/:token"
              render={(props) => (
                <Redirect
                  to={{
                    pathname: '/confirm',
                    state: { token: props.match.params.token, search: props.location.search },
                  }}
                />
              )}
            />
            <Route path="/confirm" component={ConfirmationPage} />
class ConfirmationPage extends React.Component {
      newSignup: queryString.parse(props.location.state.search).signup,
    const token = this.props.location.state.token;
export { ConfirmationPage };
export * from './ConfirmationPage';