tighten permission checks and input validation across the api
This commit is contained in:
47 files changed
+721
-225
No files matched your search
@@ -1,11 +1,11 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { join, resolve, basename } from 'path';
|
||||
import { join } from 'path';
|
||||
import { existsSync, rmSync, renameSync } from 'fs';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { getEnvironment, updateEnvironment, deleteEnvironment, getEnvironmentPublicIps, setEnvironmentPublicIp, deleteEnvironmentPublicIp, deleteEnvUpdateCheckSettings, deleteImagePruneSettings, getGitStacksForEnvironmentOnly, deleteGitStack, getBackupConfigs, getStackSources } from '$lib/server/db';
|
||||
import { getEnvironment, updateEnvironment, deleteEnvironment, getEnvironmentPublicIps, setEnvironmentPublicIp, deleteEnvironmentPublicIp, deleteEnvUpdateCheckSettings, deleteImagePruneSettings, getGitStacksForEnvironmentOnly, deleteGitStack, getBackupConfigs } from '$lib/server/db';
|
||||
import { clearDockerClientCache } from '$lib/server/docker';
|
||||
import { deleteGitStackFiles, getGitReposDir } from '$lib/server/git';
|
||||
import { getDefaultStacksDir, getLocalStacksDir, isLocalConnection, isStacksDirEnvSet } from '$lib/server/stacks';
|
||||
import { getStacksDir } from '$lib/server/stacks';
|
||||
import { authorize } from '$lib/server/authorize';
|
||||
import { auditEnvironment } from '$lib/server/audit';
|
||||
import { refreshSubprocessEnvironments } from '$lib/server/subprocess-manager';
|
||||
@@ -34,9 +34,11 @@ export const GET: RequestHandler = async ({ params, cookies }) => {
|
||||
if (auth.authEnabled && !await auth.can('environments', 'view')) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
}
|
||||
const id = parseInt(params.id);
|
||||
const envAccessDenied = await auth.requireEnvAccess(id);
|
||||
if (envAccessDenied) return envAccessDenied;
|
||||
|
||||
try {
|
||||
const id = parseInt(params.id);
|
||||
const env = await getEnvironment(id);
|
||||
|
||||
if (!env) {
|
||||
@@ -80,10 +82,11 @@ export const PUT: RequestHandler = async (event) => {
|
||||
if (auth.authEnabled && !await auth.can('environments', 'edit')) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
}
|
||||
const id = parseInt(params.id);
|
||||
const envAccessDenied = await auth.requireEnvAccess(id);
|
||||
if (envAccessDenied) return envAccessDenied;
|
||||
|
||||
try {
|
||||
const id = parseInt(params.id);
|
||||
|
||||
// Get old values before update for diff
|
||||
const oldEnv = await getEnvironment(id);
|
||||
if (!oldEnv) {
|
||||
@@ -116,7 +119,7 @@ export const PUT: RequestHandler = async (event) => {
|
||||
// regardless of where they ultimately deploy — so the rename matters
|
||||
// locally for every env type.
|
||||
if (isRename) {
|
||||
const stacksDir = getDefaultStacksDir();
|
||||
const stacksDir = getStacksDir();
|
||||
const gitReposDir = getGitReposDir();
|
||||
const oldStacks = join(stacksDir, oldEnv.name);
|
||||
const newStacks = join(stacksDir, data.name);
|
||||
@@ -241,6 +244,12 @@ export const PUT: RequestHandler = async (event) => {
|
||||
export const DELETE: RequestHandler = async (event) => {
|
||||
const { params, cookies } = event;
|
||||
const auth = await authorize(cookies);
|
||||
// Deleting an environment cascades a filesystem cleanup of its stack dirs, git
|
||||
// files, and schedules - an instance-level action, not an env-scoped one. Require
|
||||
// admin under RBAC so an env-scoped role can't remove environments it doesn't own.
|
||||
if (auth.authEnabled && auth.isEnterprise && !auth.isAdmin) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
}
|
||||
if (auth.authEnabled && !await auth.can('environments', 'delete')) {
|
||||
return json({ error: 'Permission denied' }, { status: 403 });
|
||||
}
|
||||
@@ -317,7 +326,7 @@ export const DELETE: RequestHandler = async (event) => {
|
||||
// Clean up stack directory for this environment
|
||||
// Safety: only delete subdirectory named after the env, never the parent
|
||||
try {
|
||||
const stacksDir = getDefaultStacksDir();
|
||||
const stacksDir = getStacksDir();
|
||||
const envStackDir = join(stacksDir, env.name);
|
||||
if (envStackDir !== stacksDir && envStackDir.startsWith(stacksDir) && existsSync(envStackDir)) {
|
||||
rmSync(envStackDir, { recursive: true, force: true });
|
||||
@@ -326,28 +335,6 @@ export const DELETE: RequestHandler = async (event) => {
|
||||
console.error(`Failed to clean up stack directory for environment "${env.name}":`, err);
|
||||
}
|
||||
|
||||
// Flat STACKS_DIR layout: remove per-stack managed dirs for this local environment
|
||||
if (isStacksDirEnvSet() && isLocalConnection(env)) {
|
||||
try {
|
||||
const localStacksDir = getLocalStacksDir();
|
||||
const resolvedLocalRoot = resolve(localStacksDir);
|
||||
const stackSources = await getStackSources(id);
|
||||
for (const source of stackSources) {
|
||||
const stackDir = join(localStacksDir, source.stackName);
|
||||
const resolvedStackDir = resolve(stackDir);
|
||||
if (
|
||||
resolvedStackDir.startsWith(resolvedLocalRoot + '/') &&
|
||||
basename(resolvedStackDir) === source.stackName &&
|
||||
existsSync(stackDir)
|
||||
) {
|
||||
rmSync(stackDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Failed to clean up flat STACKS_DIR stacks for environment "${env.name}":`, err);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up git-repos directory for this environment
|
||||
try {
|
||||
const gitReposDir = getGitReposDir();
|
||||
|
||||
Reference in new issue
Block a user