fix(env): resolve 404 when loading env files and CSP inline script violation (#134)

The /envs endpoint now filters to only return env files that actually exist
on disk, and absolute env_file paths from compose files (e.g. shared
globals.env in a sibling directory) are no longer rejected. The inline
theme-detection script is moved to an external file to comply with CSP.
This commit is contained in:
Anso
2026-03-25 01:03:22 -04:00
committed by GitHub
parent cb5b25de8d
commit 1e6367a147
4 changed files with 37 additions and 20 deletions
+7
View File
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Fixed
* **env:** fix 404 when loading env files for stacks with `env_file` paths outside the stack directory (e.g. shared `globals.env`). The `/envs` endpoint now only returns files that exist on disk, and absolute `env_file` paths from compose files are no longer rejected.
* **csp:** fix Content Security Policy violation caused by inline theme-detection script. Moved to an external `theme-init.js` file so it is covered by `script-src 'self'`.
## [0.2.2](https://github.com/AnsoCode/Sencho/compare/v0.2.1...v0.2.2) (2026-03-25)
+19 -8
View File
@@ -847,11 +847,6 @@ async function resolveAllEnvFilePaths(nodeId: number, stackName: string): Promis
const addEnvPath = (rawPath: string) => {
const resolved = path.resolve(stackDir, rawPath);
// Reject paths that escape the stack directory
if (!resolved.startsWith(path.resolve(stackDir) + path.sep) && resolved !== path.resolve(stackDir)) {
console.warn(`[Security] env_file path "${rawPath}" escapes stack directory — skipping`);
return;
}
envFiles.add(resolved);
};
@@ -866,15 +861,31 @@ async function resolveAllEnvFilePaths(nodeId: number, stackName: string): Promis
}
if (envFiles.size === 0) {
return [defaultEnvPath];
envFiles.add(defaultEnvPath);
}
return Array.from(envFiles);
// Filter to only include files that actually exist on disk
const existing: string[] = [];
for (const f of envFiles) {
try {
await fsService.access(f);
existing.push(f);
} catch {
// File does not exist — skip
}
}
return existing;
} catch (error) {
console.warn(`Could not parse compose.yaml for env_file resolution in stack "${stackName}":`, error);
}
return [defaultEnvPath];
// Fallback: return default only if it exists
try {
await fsService.access(defaultEnvPath);
return [defaultEnvPath];
} catch {
return [];
}
}
app.get('/api/stacks/:stackName/envs', async (req: Request, res: Response) => {
+1 -12
View File
@@ -9,18 +9,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@100..900&display=swap" rel="stylesheet" />
<script>
(function () {
try {
var saved = localStorage.getItem('sencho-theme');
if (saved === 'light') {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
} catch (e) { }
})();
</script>
<script src="/theme-init.js"></script>
</head>
<body>
+10
View File
@@ -0,0 +1,10 @@
(function () {
try {
var saved = localStorage.getItem('sencho-theme');
if (saved === 'light') {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
} catch (e) { }
})();