Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,13 @@
**Vulnerability:** Missing `object-src 'none'` and `block-all-mixed-content` in CSP.
**Learning:** Without `object-src 'none'`, attackers could potentially inject malicious `<object>` or `<embed>` elements (e.g. Flash/Java) if an injection flaw exists. Without `block-all-mixed-content`, active network attacks could downgrade HTTPS connections by loading HTTP subresources.
**Prevention:** Always include `object-src 'none'; block-all-mixed-content;` in the baseline Content-Security-Policy to proactively disable legacy plugins and enforce secure transport.

## 2026-06-28 - [Fetch Timeout Security Enhancement]
**Vulnerability:** Missing timeout on network requests in `fetchWithProgress.js`.
**Learning:** `fetch` requests without a timeout can cause the application to hang indefinitely if the server is unresponsive, potentially leading to resource exhaustion or a poor user experience.
**Prevention:** Always implement a timeout (e.g., using `AbortController`) for network requests to ensure they fail gracefully.

## 2026-06-28 - [Insecure Randomness Security Enhancement]
**Vulnerability:** Weak random number generation using `Math.random()`.
**Learning:** `Math.random()` is not cryptographically secure, which could lead to predictable outcomes in simulation generation or terrain seeding.
**Prevention:** Use `window.crypto.getRandomValues()` instead of `Math.random()` to ensure strong random number generation for security or unpredictability.
16 changes: 11 additions & 5 deletions src/simulation/fetchWithProgress.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,18 @@ export async function readResponseBodyWithProgress(response, onProgress) {
* @param {(ratio: number) => void} [onProgress]
* @returns {Promise<Uint8Array>}
*/
export async function fetchUint8ArrayWithProgress(url, onProgress) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch ${url}: ${response.status}`);
export async function fetchUint8ArrayWithProgress(url, onProgress, timeoutMs = 30000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) {
throw new Error(`Failed to fetch ${url}: ${response.status}`);
}
return await readResponseBodyWithProgress(response, onProgress);
} finally {
clearTimeout(timeoutId);
}
return readResponseBodyWithProgress(response, onProgress);
}

/**
Expand Down
5 changes: 4 additions & 1 deletion src/simulation/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,10 @@ export class MuJoCoDemo {
if (!this.simulation || !this.model) {
return;
}
const theta = Math.random() * Math.PI * 2;
const cryptoRandom = typeof window !== 'undefined' && window.crypto
? window.crypto.getRandomValues(new Uint32Array(1))[0] / 4294967296
: Math.random();
const theta = cryptoRandom * Math.PI * 2;
this._knockdownDirX = Math.cos(theta);
this._knockdownDirY = Math.sin(theta);
this._knockdownSubstepsRemaining = 14;
Expand Down
5 changes: 4 additions & 1 deletion src/views/Demo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1825,7 +1825,10 @@ export default {
this.bfmSimTime = null;
},
bfmTerrainRandomize() {
this.bfmTerrainSeed = Math.floor(Math.random() * 1e9) + 1;
const cryptoRandom = typeof window !== 'undefined' && window.crypto
? window.crypto.getRandomValues(new Uint32Array(1))[0] / 4294967296
: Math.random();
this.bfmTerrainSeed = Math.floor(cryptoRandom * 1e9) + 1;
},
bfmTerrainGenerate() {
const cfg = {
Expand Down
Loading