registri dei piani in git (AI-OS#309)
Refs gitea:Adriano/AI-OS#310 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# Review package: e2bbc90..7211d74
|
||||
|
||||
## Commits
|
||||
7211d74 fix(sicurezza): safeNext blocca backslash (open redirect) + guardia ownership su /admin/edit
|
||||
|
||||
## Files changed
|
||||
src/lib/safe-next.ts | 1 +
|
||||
src/pages/admin/edit/[id].astro | 4 +++-
|
||||
tests/safe-next.test.ts | 2 ++
|
||||
3 files changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
## Diff
|
||||
diff --git a/src/lib/safe-next.ts b/src/lib/safe-next.ts
|
||||
index b9b9a6c..1c2e5b3 100644
|
||||
--- a/src/lib/safe-next.ts
|
||||
+++ b/src/lib/safe-next.ts
|
||||
@@ -1,7 +1,8 @@
|
||||
// Path interno sicuro per il redirect post-login: deve iniziare con "/", non essere
|
||||
// protocol-relative ("//"), non puntare alle API. Tutto il resto → home.
|
||||
export function safeNext(next: string | null): string {
|
||||
+ if (next && next.includes('\\')) return '/';
|
||||
if (!next || !next.startsWith('/') || next.startsWith('//')) return '/';
|
||||
if (next.startsWith('/api/')) return '/';
|
||||
return next;
|
||||
}
|
||||
diff --git a/src/pages/admin/edit/[id].astro b/src/pages/admin/edit/[id].astro
|
||||
index 7789e84..a3a0858 100644
|
||||
--- a/src/pages/admin/edit/[id].astro
|
||||
+++ b/src/pages/admin/edit/[id].astro
|
||||
@@ -1,14 +1,16 @@
|
||||
---
|
||||
import Admin from '../../../layouts/Admin.astro';
|
||||
import PostForm from '../../../components/admin/PostForm.astro';
|
||||
import { getDb } from '../../../lib/db';
|
||||
-import { getPostById } from '../../../lib/posts';
|
||||
+import { getPostById, canModifyPost } from '../../../lib/posts';
|
||||
export const prerender = false;
|
||||
const post = getPostById(getDb(), Number(Astro.params.id));
|
||||
if (!post) return Astro.redirect('/admin');
|
||||
+const u = Astro.locals.user!;
|
||||
+if (!canModifyPost(u.role, u.id, post)) return Astro.redirect('/admin');
|
||||
---
|
||||
<Admin title={`Modifica: ${post.title}`}>
|
||||
<h1>Modifica articolo</h1>
|
||||
<script id="initial-body" type="text/plain" set:html={post.body_html}></script>
|
||||
<PostForm post={post} />
|
||||
</Admin>
|
||||
diff --git a/tests/safe-next.test.ts b/tests/safe-next.test.ts
|
||||
index 9435af9..c0aeafc 100644
|
||||
--- a/tests/safe-next.test.ts
|
||||
+++ b/tests/safe-next.test.ts
|
||||
@@ -7,12 +7,14 @@ describe('safeNext', () => {
|
||||
expect(safeNext('/blog/x?y=1')).toBe('/blog/x?y=1');
|
||||
});
|
||||
it('rifiuta esterni, protocol-relative, api e vuoti', () => {
|
||||
expect(safeNext(null)).toBe('/');
|
||||
expect(safeNext('')).toBe('/');
|
||||
expect(safeNext('//evil.com')).toBe('/');
|
||||
expect(safeNext('https://evil.com')).toBe('/');
|
||||
expect(safeNext('/api/admin/posts')).toBe('/');
|
||||
expect(safeNext('javascript:alert(1)')).toBe('/');
|
||||
expect(safeNext('not-a-path')).toBe('/');
|
||||
+ expect(safeNext('/\\evil.com')).toBe('/');
|
||||
+ expect(safeNext('/\\/evil.com')).toBe('/');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user