feat: Implement secure random generation for session IDs and card shuffling

This commit is contained in:
Morax
2025-07-31 10:35:45 +08:00
parent 1ab822e58d
commit 6fe7e380cb
3 changed files with 32 additions and 2 deletions

View File

@@ -0,0 +1,13 @@
{
"permissions": {
"allow": [
"Bash(ls:*)",
"Bash(npm outdated:*)",
"Bash(jq:*)",
"Bash(node:*)",
"Bash(npm run build:*)",
"Bash(npm test)"
],
"deny": []
}
}

View File

@@ -1,4 +1,5 @@
import { TarotCard } from './types.js'; import { TarotCard } from './types.js';
import { getSecureRandom } from './utils.js';
export interface SearchOptions { export interface SearchOptions {
keyword?: string; keyword?: string;
@@ -103,7 +104,8 @@ export class TarotCardSearch {
filteredCards = searchResults.map(result => result.card); filteredCards = searchResults.map(result => result.card);
} }
const shuffled = [...filteredCards].sort(() => Math.random() - 0.5); // Use Fisher-Yates shuffle with secure random for true randomness
const shuffled = this.fisherYatesShuffle(filteredCards);
return shuffled.slice(0, Math.min(count, shuffled.length)); return shuffled.slice(0, Math.min(count, shuffled.length));
} }
@@ -250,4 +252,16 @@ export class TarotCardSearch {
.sort((a, b) => b.count - a.count) .sort((a, b) => b.count - a.count)
.slice(0, limit); .slice(0, limit);
} }
/**
* Fisher-Yates shuffle algorithm using cryptographically secure random
*/
private fisherYatesShuffle<T>(array: readonly T[]): T[] {
const shuffled = [...array];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(getSecureRandom() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
}
} }

View File

@@ -1,4 +1,5 @@
import { TarotSession, TarotReading } from "./types.js"; import { TarotSession, TarotReading } from "./types.js";
import { getSecureRandom } from "./utils.js";
/** /**
* Manages tarot reading sessions * Manages tarot reading sessions
@@ -69,7 +70,9 @@ export class TarotSessionManager {
* Generate a unique session ID * Generate a unique session ID
*/ */
private generateSessionId(): string { private generateSessionId(): string {
return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const timestamp = Date.now();
const randomPart = Math.floor(getSecureRandom() * 1000000000).toString(36);
return `session_${timestamp}_${randomPart}`;
} }
/** /**