feat: Refactor random number generation to use a secure method from utils
This commit is contained in:
@@ -2,9 +2,7 @@ import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { TarotCard, CardOrientation, CardCategory } from './types.js';
|
||||
|
||||
// Use global crypto in Node.js >= 18 and browsers
|
||||
declare const crypto: any;
|
||||
import { getSecureRandom } from './utils.js';
|
||||
|
||||
// Helper to get __dirname in ES modules
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -206,19 +204,6 @@ export class TarotCardManager {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a cryptographically secure random number between 0 and 1.
|
||||
*/
|
||||
private getSecureRandom(): number {
|
||||
if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {
|
||||
const array = new Uint32Array(1);
|
||||
crypto.getRandomValues(array);
|
||||
return array[0] / (0xffffffff + 1);
|
||||
}
|
||||
// Fallback for older Node.js or unexpected environments
|
||||
console.warn('crypto.getRandomValues not available, falling back to Math.random().');
|
||||
return Math.random();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fisher-Yates shuffle algorithm for true randomness.
|
||||
@@ -226,7 +211,7 @@ export class TarotCardManager {
|
||||
private fisherYatesShuffle<T>(array: readonly T[]): T[] {
|
||||
const shuffled = [...array];
|
||||
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(this.getSecureRandom() * (i + 1));
|
||||
const j = Math.floor(getSecureRandom() * (i + 1));
|
||||
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
||||
}
|
||||
return shuffled;
|
||||
@@ -236,7 +221,7 @@ export class TarotCardManager {
|
||||
* Get a random card from the deck.
|
||||
*/
|
||||
public getRandomCard(): TarotCard {
|
||||
const randomIndex = Math.floor(this.getSecureRandom() * this.allCards.length);
|
||||
const randomIndex = Math.floor(getSecureRandom() * this.allCards.length);
|
||||
return this.allCards[randomIndex];
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ import { TarotCardManager } from "./card-manager.js";
|
||||
import { TarotSessionManager } from "./session-manager.js";
|
||||
import { TarotReading, DrawnCard, CardOrientation, TarotCard } from "./types.js";
|
||||
import { TAROT_SPREADS, getAllSpreads, getSpread, isValidSpreadType } from "./spreads.js";
|
||||
import { getSecureRandom } from "./utils.js";
|
||||
|
||||
/**
|
||||
* Manages tarot readings and interpretations
|
||||
@@ -1115,41 +1116,17 @@ export class TarotReadingManager {
|
||||
*/
|
||||
private getSecureRandomOrientation(): CardOrientation {
|
||||
// Use the same secure random method as card manager
|
||||
const random = this.getSecureRandom();
|
||||
const random = getSecureRandom();
|
||||
return random < 0.5 ? "upright" : "reversed"; // 50% chance upright, 50% reversed
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate cryptographically secure random number
|
||||
*/
|
||||
private getSecureRandom(): number {
|
||||
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
||||
// Browser environment with Web Crypto API
|
||||
const array = new Uint32Array(1);
|
||||
crypto.getRandomValues(array);
|
||||
return array[0] / (0xffffffff + 1);
|
||||
} else if (typeof require !== 'undefined') {
|
||||
// Node.js environment
|
||||
try {
|
||||
const crypto = require('crypto');
|
||||
return crypto.randomBytes(4).readUInt32BE(0) / (0xffffffff + 1);
|
||||
} catch (e) {
|
||||
// Fallback to Math.random if crypto is not available
|
||||
console.warn('Crypto module not available, falling back to Math.random()');
|
||||
return Math.random();
|
||||
}
|
||||
} else {
|
||||
// Fallback to Math.random
|
||||
return Math.random();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique reading ID with secure randomness
|
||||
*/
|
||||
private generateReadingId(): string {
|
||||
const timestamp = Date.now();
|
||||
const randomPart = Math.floor(this.getSecureRandom() * 1000000000).toString(36);
|
||||
const randomPart = Math.floor(getSecureRandom() * 1000000000).toString(36);
|
||||
return `reading_${timestamp}_${randomPart}`;
|
||||
}
|
||||
}
|
||||
|
15
src/tarot/utils.ts
Normal file
15
src/tarot/utils.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// Use global crypto in Node.js >= 18 and browsers
|
||||
declare const crypto: any;
|
||||
|
||||
/**
|
||||
* Generate a cryptographically secure random number between 0 and 1.
|
||||
* @throws {Error} If crypto.getRandomValues is not available.
|
||||
*/
|
||||
export function getSecureRandom(): number {
|
||||
if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {
|
||||
const array = new Uint32Array(1);
|
||||
crypto.getRandomValues(array);
|
||||
return array[0] / (0xffffffff + 1);
|
||||
}
|
||||
throw new Error('A secure random number generator (crypto.getRandomValues) is not available in this environment.');
|
||||
}
|
Reference in New Issue
Block a user