Compare commits

...

3 Commits

Author SHA1 Message Date
Owen
874794c996 Clean email 2026-02-18 14:07:50 -08:00
Owen
5e37c4e85f Resolve potential issues with processing roleIds 2026-02-18 13:55:04 -08:00
Owen
4e7eac368f Uniform ne check on niceId and dont reject clients 2026-02-18 11:56:01 -08:00
6 changed files with 62 additions and 39 deletions

View File

@@ -23,9 +23,14 @@ export async function verifyApiKeyRoleAccess(
); );
} }
const { roleIds } = req.body; let allRoleIds: number[] = [];
const allRoleIds = if (!isNaN(singleRoleId)) {
roleIds || (isNaN(singleRoleId) ? [] : [singleRoleId]); // If roleId is provided in URL params, query params, or body (single), use it exclusively
allRoleIds = [singleRoleId];
} else if (req.body?.roleIds) {
// Only use body.roleIds if no single roleId was provided
allRoleIds = req.body.roleIds;
}
if (allRoleIds.length === 0) { if (allRoleIds.length === 0) {
return next(); return next();

View File

@@ -23,8 +23,14 @@ export async function verifyRoleAccess(
); );
} }
const roleIds = req.body?.roleIds; let allRoleIds: number[] = [];
const allRoleIds = roleIds || (isNaN(singleRoleId) ? [] : [singleRoleId]); if (!isNaN(singleRoleId)) {
// If roleId is provided in URL params, query params, or body (single), use it exclusively
allRoleIds = [singleRoleId];
} else if (req.body?.roleIds) {
// Only use body.roleIds if no single roleId was provided
allRoleIds = req.body.roleIds;
}
if (allRoleIds.length === 0) { if (allRoleIds.length === 0) {
return next(); return next();

View File

@@ -139,7 +139,7 @@ export async function signSshKey(
if (!userOrg.pamUsername) { if (!userOrg.pamUsername) {
if (req.user?.email) { if (req.user?.email) {
// Extract username from email (first part before @) // Extract username from email (first part before @)
usernameToUse = req.user?.email.split("@")[0]; usernameToUse = req.user?.email.split("@")[0].replace(/[^a-zA-Z0-9_-]/g, "");
if (!usernameToUse) { if (!usernameToUse) {
return next( return next(
createHttpError( createHttpError(

View File

@@ -6,7 +6,7 @@ import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import logger from "@server/logger"; import logger from "@server/logger";
import { eq, and } from "drizzle-orm"; import { eq, and, ne } from "drizzle-orm";
import { fromError } from "zod-validation-error"; import { fromError } from "zod-validation-error";
import { OpenAPITags, registry } from "@server/openApi"; import { OpenAPITags, registry } from "@server/openApi";
@@ -93,7 +93,8 @@ export async function updateClient(
.where( .where(
and( and(
eq(clients.niceId, niceId), eq(clients.niceId, niceId),
eq(clients.orgId, clients.orgId) eq(clients.orgId, clients.orgId),
ne(clients.clientId, clientId)
) )
) )
.limit(1); .limit(1);

View File

@@ -9,7 +9,7 @@ import {
Resource, Resource,
resources resources
} from "@server/db"; } from "@server/db";
import { eq, and } from "drizzle-orm"; import { eq, and, ne } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -33,7 +33,15 @@ const updateResourceParamsSchema = z.strictObject({
const updateHttpResourceBodySchema = z const updateHttpResourceBodySchema = z
.strictObject({ .strictObject({
name: z.string().min(1).max(255).optional(), name: z.string().min(1).max(255).optional(),
niceId: z.string().min(1).max(255).regex(/^[a-zA-Z0-9-]+$/, "niceId can only contain letters, numbers, and dashes").optional(), niceId: z
.string()
.min(1)
.max(255)
.regex(
/^[a-zA-Z0-9-]+$/,
"niceId can only contain letters, numbers, and dashes"
)
.optional(),
subdomain: subdomainSchema.nullable().optional(), subdomain: subdomainSchema.nullable().optional(),
ssl: z.boolean().optional(), ssl: z.boolean().optional(),
sso: z.boolean().optional(), sso: z.boolean().optional(),
@@ -248,14 +256,13 @@ async function updateHttpResource(
.where( .where(
and( and(
eq(resources.niceId, updateData.niceId), eq(resources.niceId, updateData.niceId),
eq(resources.orgId, resource.orgId) eq(resources.orgId, resource.orgId),
ne(resources.resourceId, resource.resourceId) // exclude the current resource from the search
) )
); )
.limit(1);
if ( if (existingResource) {
existingResource &&
existingResource.resourceId !== resource.resourceId
) {
return next( return next(
createHttpError( createHttpError(
HttpCode.CONFLICT, HttpCode.CONFLICT,
@@ -343,7 +350,10 @@ async function updateHttpResource(
headers = null; headers = null;
} }
const isLicensed = await isLicensedOrSubscribed(resource.orgId, tierMatrix.maintencePage); const isLicensed = await isLicensedOrSubscribed(
resource.orgId,
tierMatrix.maintencePage
);
if (!isLicensed) { if (!isLicensed) {
updateData.maintenanceModeEnabled = undefined; updateData.maintenanceModeEnabled = undefined;
updateData.maintenanceModeType = undefined; updateData.maintenanceModeType = undefined;

View File

@@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { sites } from "@server/db"; import { sites } from "@server/db";
import { eq, and } from "drizzle-orm"; import { eq, and, ne } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -19,8 +19,8 @@ const updateSiteBodySchema = z
.strictObject({ .strictObject({
name: z.string().min(1).max(255).optional(), name: z.string().min(1).max(255).optional(),
niceId: z.string().min(1).max(255).optional(), niceId: z.string().min(1).max(255).optional(),
dockerSocketEnabled: z.boolean().optional(), dockerSocketEnabled: z.boolean().optional()
remoteSubnets: z.string().optional() // remoteSubnets: z.string().optional()
// subdomain: z // subdomain: z
// .string() // .string()
// .min(1) // .min(1)
@@ -86,18 +86,19 @@ export async function updateSite(
// if niceId is provided, check if it's already in use by another site // if niceId is provided, check if it's already in use by another site
if (updateData.niceId) { if (updateData.niceId) {
const existingSite = await db const [existingSite] = await db
.select() .select()
.from(sites) .from(sites)
.where( .where(
and( and(
eq(sites.niceId, updateData.niceId), eq(sites.niceId, updateData.niceId),
eq(sites.orgId, sites.orgId) eq(sites.orgId, sites.orgId),
ne(sites.siteId, siteId)
) )
) )
.limit(1); .limit(1);
if (existingSite.length > 0 && existingSite[0].siteId !== siteId) { if (existingSite) {
return next( return next(
createHttpError( createHttpError(
HttpCode.CONFLICT, HttpCode.CONFLICT,
@@ -107,22 +108,22 @@ export async function updateSite(
} }
} }
// if remoteSubnets is provided, ensure it's a valid comma-separated list of cidrs // // if remoteSubnets is provided, ensure it's a valid comma-separated list of cidrs
if (updateData.remoteSubnets) { // if (updateData.remoteSubnets) {
const subnets = updateData.remoteSubnets // const subnets = updateData.remoteSubnets
.split(",") // .split(",")
.map((s) => s.trim()); // .map((s) => s.trim());
for (const subnet of subnets) { // for (const subnet of subnets) {
if (!isValidCIDR(subnet)) { // if (!isValidCIDR(subnet)) {
return next( // return next(
createHttpError( // createHttpError(
HttpCode.BAD_REQUEST, // HttpCode.BAD_REQUEST,
`Invalid CIDR format: ${subnet}` // `Invalid CIDR format: ${subnet}`
) // )
); // );
} // }
} // }
} // }
const updatedSite = await db const updatedSite = await db
.update(sites) .update(sites)