[FIX] Fix some securiry issues
This commit is contained in:
@@ -3,7 +3,10 @@ import { auth } from '$lib/stores/auth';
|
||||
import { browser } from '$app/environment';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
const API_BASE_URL = browser ? 'http://localhost:8000' : 'http://localhost:8000';
|
||||
// Use environment variable or default to localhost
|
||||
const API_BASE_URL = browser
|
||||
? (import.meta.env.VITE_API_URL || 'http://localhost:8000')
|
||||
: (import.meta.env.VITE_API_URL || 'http://localhost:8000');
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
@@ -107,15 +110,21 @@ export const apiService = {
|
||||
},
|
||||
|
||||
getDownloadUrl(path: string): string {
|
||||
let token = '';
|
||||
auth.subscribe((state) => {
|
||||
token = state.token || '';
|
||||
})();
|
||||
return `${API_BASE_URL}/download/${path}?token=${token}`;
|
||||
// Security: Token is now passed via Authorization header, not URL
|
||||
// The backend will read the token from the header in the request
|
||||
return `${API_BASE_URL}/download/${path}`;
|
||||
},
|
||||
|
||||
async createScore(name: string, compositor: string): Promise<{ success: boolean; score?: any; error?: string }> {
|
||||
const response = await api.post('/admin/scores', { name, compositor });
|
||||
// New method to download with proper auth header
|
||||
async downloadFileWithAuth(path: string): Promise<Blob> {
|
||||
const response = await api.get(`/download/${path}`, {
|
||||
responseType: 'blob'
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async createScore(name: string, compositor: string, pieces: { number: number; name: string }[] = []): Promise<{ success: boolean; score?: any; error?: string }> {
|
||||
const response = await api.post('/admin/scores', { name, compositor, pieces });
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -146,5 +155,17 @@ export const apiService = {
|
||||
}
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async getScoreFiles(scoreId: string): Promise<{ success: boolean; files: any[]; error?: string }> {
|
||||
const response = await api.get(`/admin/scores/${scoreId}/files`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async deleteScoreFile(scoreId: string, filePath: string): Promise<{ success: boolean; error?: string }> {
|
||||
const response = await api.delete(`/admin/scores/${scoreId}/files`, {
|
||||
params: { path: filePath }
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,9 +11,34 @@ interface AuthState {
|
||||
user: User | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth Store
|
||||
*
|
||||
* SECURITY NOTE: Token is stored in localStorage which is vulnerable to XSS attacks.
|
||||
* The CSP (Content Security Policy) helps mitigate XSS risks.
|
||||
* For production with higher security requirements, consider migrating to httpOnly cookies.
|
||||
*/
|
||||
function createAuthStore() {
|
||||
// SECURITY: Validate stored data before parsing to prevent XSS via localStorage
|
||||
const stored = browser ? localStorage.getItem('auth') : null;
|
||||
const initial: AuthState = stored ? JSON.parse(stored) : { token: null, user: null };
|
||||
let initial: AuthState = { token: null, user: null };
|
||||
|
||||
if (stored) {
|
||||
try {
|
||||
const parsed = JSON.parse(stored);
|
||||
// Validate structure to prevent injection
|
||||
if (parsed && typeof parsed === 'object' &&
|
||||
(parsed.token === null || typeof parsed.token === 'string') &&
|
||||
(parsed.user === null || (typeof parsed.user === 'object' && parsed.user.username && parsed.user.role))) {
|
||||
initial = parsed;
|
||||
}
|
||||
} catch (e) {
|
||||
// Invalid stored data, clear it
|
||||
if (browser) {
|
||||
localStorage.removeItem('auth');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { subscribe, set, update } = writable<AuthState>(initial);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user