fix potential open redirect

deepsource-autofix-76c6eb20
Dan Gowans 2022-09-22 11:35:47 -04:00
parent 3140039d5c
commit eaa28441ac
7 changed files with 88 additions and 75 deletions

4
app.js
View File

@ -24,6 +24,7 @@ import { version } from "./version.js";
import * as databaseInitializer from "./helpers/initializer.database.js"; import * as databaseInitializer from "./helpers/initializer.database.js";
import debug from "debug"; import debug from "debug";
import { apiGetHandler } from "./handlers/permissions.js"; import { apiGetHandler } from "./handlers/permissions.js";
import { getSafeRedirectURL } from "./helpers/functions.authentication.js";
const debugApp = debug("lot-occupancy-system:app"); const debugApp = debug("lot-occupancy-system:app");
databaseInitializer.initializeDatabase(); databaseInitializer.initializeDatabase();
const __dirname = "."; const __dirname = ".";
@ -91,7 +92,8 @@ const sessionChecker = (request, response, next) => {
if (request.session.user && request.cookies[sessionCookieName]) { if (request.session.user && request.cookies[sessionCookieName]) {
return next(); return next();
} }
return response.redirect(`${urlPrefix}/login?redirect=${request.originalUrl}`); const redirectUrl = getSafeRedirectURL(request.originalUrl);
return response.redirect(`${urlPrefix}/login?redirect=${redirectUrl}`);
}; };
app.use((request, response, next) => { app.use((request, response, next) => {
response.locals.buildNumber = version; response.locals.buildNumber = version;

5
app.ts
View File

@ -31,6 +31,7 @@ import * as databaseInitializer from "./helpers/initializer.database.js";
import debug from "debug"; import debug from "debug";
import { apiGetHandler } from "./handlers/permissions.js"; import { apiGetHandler } from "./handlers/permissions.js";
import { getSafeRedirectURL } from "./helpers/functions.authentication.js";
const debugApp = debug("lot-occupancy-system:app"); const debugApp = debug("lot-occupancy-system:app");
/* /*
@ -177,8 +178,10 @@ const sessionChecker = (
return next(); return next();
} }
const redirectUrl = getSafeRedirectURL(request.originalUrl);
return response.redirect( return response.redirect(
`${urlPrefix}/login?redirect=${request.originalUrl}` `${urlPrefix}/login?redirect=${redirectUrl}`
); );
}; };

View File

@ -1 +1,2 @@
export declare const authenticate: (userName: string, password: string) => Promise<boolean>; export declare const authenticate: (userName: string, password: string) => Promise<boolean>;
export declare const getSafeRedirectURL: (possibleRedirectURL?: string) => string;

View File

@ -24,3 +24,36 @@ export const authenticate = async (userName, password) => {
} }
return await authenticateViaActiveDirectory(userName, password); return await authenticateViaActiveDirectory(userName, password);
}; };
const safeRedirects = new Set([
"/admin/cleanup",
"/admin/fees",
"/admin/occupancytypes",
"/admin/tables",
"/lotoccupancies",
"/lotoccupancies/new",
"/lots",
"/lots/new",
"/maps",
"/maps/new",
"/workorders",
"/workorders/new",
"/workorders/milestonecalendar",
"/workorders/outlook",
"/reports"
]);
export const getSafeRedirectURL = (possibleRedirectURL = "") => {
const urlPrefix = configFunctions.getProperty("reverseProxy.urlPrefix");
if (typeof possibleRedirectURL === "string") {
const urlToCheck = (possibleRedirectURL.startsWith(urlPrefix)
? possibleRedirectURL.slice(urlPrefix.length)
: possibleRedirectURL).toLowerCase();
if (safeRedirects.has(urlToCheck) ||
/^(\/maps\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/lots\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/lotoccupancies\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/workorders\/)\d+(\/edit)?$/.test(urlToCheck)) {
return urlPrefix + urlToCheck;
}
}
return urlPrefix + "/dashboard";
};

View File

@ -41,3 +41,46 @@ export const authenticate = async (
return await authenticateViaActiveDirectory(userName, password); return await authenticateViaActiveDirectory(userName, password);
}; };
const safeRedirects = new Set([
"/admin/cleanup",
"/admin/fees",
"/admin/occupancytypes",
"/admin/tables",
"/lotoccupancies",
"/lotoccupancies/new",
"/lots",
"/lots/new",
"/maps",
"/maps/new",
"/workorders",
"/workorders/new",
"/workorders/milestonecalendar",
"/workorders/outlook",
"/reports"
]);
export const getSafeRedirectURL = (possibleRedirectURL = "") => {
const urlPrefix = configFunctions.getProperty("reverseProxy.urlPrefix");
if (typeof possibleRedirectURL === "string") {
const urlToCheck = (
possibleRedirectURL.startsWith(urlPrefix)
? possibleRedirectURL.slice(urlPrefix.length)
: possibleRedirectURL
).toLowerCase();
if (
safeRedirects.has(urlToCheck) ||
/^(\/maps\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/lots\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/lotoccupancies\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/workorders\/)\d+(\/edit)?$/.test(urlToCheck)
) {
return urlPrefix + urlToCheck;
}
}
return urlPrefix + "/dashboard";
};

View File

@ -6,42 +6,12 @@ import { getApiKey } from "../helpers/functions.api.js";
import Debug from "debug"; import Debug from "debug";
const debug = Debug("lot-occupancy-system:login"); const debug = Debug("lot-occupancy-system:login");
export const router = Router(); export const router = Router();
const safeRedirects = new Set([
"/admin/fees",
"/admin/occupancytypes",
"/admin/tables",
"/lotoccupancies",
"/lotoccupancies/new",
"/lots",
"/lots/new",
"/maps",
"/maps/new",
"/workorders",
"/workorders/new",
"/reports"
]);
const getSafeRedirectURL = (possibleRedirectURL = "") => {
const urlPrefix = configFunctions.getProperty("reverseProxy.urlPrefix");
if (typeof possibleRedirectURL === "string") {
const urlToCheck = (possibleRedirectURL.startsWith(urlPrefix)
? possibleRedirectURL.slice(urlPrefix.length)
: possibleRedirectURL).toLowerCase();
if (safeRedirects.has(urlToCheck) ||
/^(\/maps\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/lots\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/lotoccupancies\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/workorders\/)\d+(\/edit)?$/.test(urlToCheck)) {
return urlPrefix + urlToCheck;
}
}
return urlPrefix + "/dashboard";
};
router router
.route("/") .route("/")
.get((request, response) => { .get((request, response) => {
const sessionCookieName = configFunctions.getProperty("session.cookieName"); const sessionCookieName = configFunctions.getProperty("session.cookieName");
if (request.session.user && request.cookies[sessionCookieName]) { if (request.session.user && request.cookies[sessionCookieName]) {
const redirectURL = getSafeRedirectURL((request.query.redirect || "")); const redirectURL = authenticationFunctions.getSafeRedirectURL((request.query.redirect || ""));
response.redirect(redirectURL); response.redirect(redirectURL);
} }
else { else {
@ -57,7 +27,7 @@ router
const userName = request.body.userName; const userName = request.body.userName;
const passwordPlain = request.body.password; const passwordPlain = request.body.password;
const unsafeRedirectURL = request.body.redirect; const unsafeRedirectURL = request.body.redirect;
const redirectURL = getSafeRedirectURL(typeof unsafeRedirectURL === "string" ? unsafeRedirectURL : ""); const redirectURL = authenticationFunctions.getSafeRedirectURL(typeof unsafeRedirectURL === "string" ? unsafeRedirectURL : "");
let isAuthenticated = false; let isAuthenticated = false;
if (userName.charAt(0) === "*") { if (userName.charAt(0) === "*") {
if (useTestDatabases && userName === passwordPlain) { if (useTestDatabases && userName === passwordPlain) {

View File

@ -16,45 +16,6 @@ const debug = Debug("lot-occupancy-system:login");
export const router = Router(); export const router = Router();
const safeRedirects = new Set([
"/admin/fees",
"/admin/occupancytypes",
"/admin/tables",
"/lotoccupancies",
"/lotoccupancies/new",
"/lots",
"/lots/new",
"/maps",
"/maps/new",
"/workorders",
"/workorders/new",
"/reports"
]);
const getSafeRedirectURL = (possibleRedirectURL = "") => {
const urlPrefix = configFunctions.getProperty("reverseProxy.urlPrefix");
if (typeof possibleRedirectURL === "string") {
const urlToCheck = (
possibleRedirectURL.startsWith(urlPrefix)
? possibleRedirectURL.slice(urlPrefix.length)
: possibleRedirectURL
).toLowerCase();
if (
safeRedirects.has(urlToCheck) ||
/^(\/maps\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/lots\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/lotoccupancies\/)\d+(\/edit)?$/.test(urlToCheck) ||
/^(\/workorders\/)\d+(\/edit)?$/.test(urlToCheck)
) {
return urlPrefix + urlToCheck;
}
}
return urlPrefix + "/dashboard";
};
router router
.route("/") .route("/")
.get((request, response) => { .get((request, response) => {
@ -62,7 +23,7 @@ router
configFunctions.getProperty("session.cookieName"); configFunctions.getProperty("session.cookieName");
if (request.session.user && request.cookies[sessionCookieName]) { if (request.session.user && request.cookies[sessionCookieName]) {
const redirectURL = getSafeRedirectURL( const redirectURL = authenticationFunctions.getSafeRedirectURL(
(request.query.redirect || "") as string (request.query.redirect || "") as string
); );
@ -82,7 +43,7 @@ router
const unsafeRedirectURL = request.body.redirect; const unsafeRedirectURL = request.body.redirect;
const redirectURL = getSafeRedirectURL( const redirectURL = authenticationFunctions.getSafeRedirectURL(
typeof unsafeRedirectURL === "string" ? unsafeRedirectURL : "" typeof unsafeRedirectURL === "string" ? unsafeRedirectURL : ""
); );