collective

Browse / github.com/storytold/artcraft-services / patch

chore: mark UI-only Math.random uses as reviewed (SonarCloud S2245)

chore: mark UI-only Math.random uses as reviewed (SonarCloud S2245) Resolves the 71 `typescript:S2245` findings in the frontend. Every site is UI-only: element and upload ids, title suffixes (`reference-audio-…`), suggested usernames, a cache buster, the 3D camera's initial jitter and the sidebar's demo width. None feeds a token, credential or security decision, so each is marked `NOSONAR` with that reason rather than switched to `crypto`, which would change id formats and add a secure-context requirement for no security gain. Four sites sat inside multi-line template literals; the random suffix was hoisted to a named const so the marker could not land insid

Status
open
Score
0.00
Adopters
0 other operators
Author
dylanmtaylor (imported from pull request #180, open)
Base
e57ee3d63ac478a24149384bf1473ef97aa067f8
Size
+75 −79 in 32 files
Review
pending
Shared
2026-10-28
Id
74bed4fd54657096571357d1

How it went

No other operator has reported using it yet.

Diff

raw · frontend/apps/artcraft-dashboard/src/components/ui/sidebar.tsx frontend/apps/artcraft-webapp/src/components/auth/signup-form.tsx frontend/apps/artcraft-webapp/src/components/prompt-box/ImagePromptRow.tsx frontend/apps/artcraft-webapp/src/components/prompt-box/MediaReferenceRow.tsx frontend/apps/artcraft-webapp/src/pages/create-audio/create-audio.tsx frontend/apps/artcraft-webapp/src/pages/create-object/components/mesh-upload.ts frontend/apps/artcraft-webapp/src/pages/create-object/create-object.tsx frontend/apps/artcraft-webapp/src/pages/create-vfx/create-vfx.tsx frontend/apps/artcraft-webapp/src/pages/create-video/create-video.tsx frontend/apps/artcraft-webapp/src/pages/create-world/components/ReferenceVideoSlot.tsx frontend/apps/artcraft-webapp/src/pages/create-world/create-world.tsx frontend/apps/artcraft-webapp/src/pages/pagedraw/base-image-selector.tsx frontend/apps/artcraft-website-next/src/components/campaign/auth-gate-modal.tsx frontend/apps/genhub/src/hooks/use-auth.tsx frontend/libs/components/image-input/src/lib/image-input.tsx frontend/libs/components/modal/src/lib/modal.tsx frontend/libs/components/pagedraw/src/lib/HistoryStack.tsx frontend/libs/components/pagedraw/src/lib/PageDraw.tsx frontend/libs/components/pagedraw/src/lib/PaintSurface.tsx frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx frontend/libs/components/promptbox/src/lib/common/AudioReferenceRow.tsx frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx frontend/libs/components/vfx/src/lib/PromptBoxVFX.tsx frontend/libs/components/vfx/src/lib/api.ts frontend/libs/components/vfx/src/lib/store.ts frontend/libs/components/video-editor/src/lib/adapters/default/indexeddb-project-storage.ts frontend/libs/components/video-editor/src/lib/adapters/default/local-file-media-source.ts

diff --git a/frontend/apps/artcraft-dashboard/src/components/ui/sidebar.tsx b/frontend/apps/artcraft-dashboard/src/components/ui/sidebar.tsx--- a/frontend/apps/artcraft-dashboard/src/components/ui/sidebar.tsx+++ b/frontend/apps/artcraft-dashboard/src/components/ui/sidebar.tsx@@ -609,7 +609,7 @@ function SidebarMenuSkeleton({ }) {   // Random width between 50 to 90%.   const width = React.useMemo(() => {-    return `${Math.floor(Math.random() * 40) + 50}%`;+    return `${Math.floor(Math.random() * 40) + 50}%`; // NOSONAR: UI-only id or cosmetic value, never a secret.   }, []);    return (diff --git a/frontend/apps/artcraft-webapp/src/components/auth/signup-form.tsx b/frontend/apps/artcraft-webapp/src/components/auth/signup-form.tsx--- a/frontend/apps/artcraft-webapp/src/components/auth/signup-form.tsx+++ b/frontend/apps/artcraft-webapp/src/components/auth/signup-form.tsx@@ -61,7 +61,7 @@ export const SignupForm = ({     const emailPrefix = email.split("@")[0].replace(/[^a-zA-Z0-9]/g, "");     // Ensure we don't exceed max length (7 chars from email + 3 digits = 10 total)     const truncatedPrefix = emailPrefix.substring(0, 7);-    const randomSuffix = Math.floor(Math.random() * 900) + 100; // 100-999+    const randomSuffix = Math.floor(Math.random() * 900) + 100; // NOSONAR: 100-999 display suffix, never a secret.     const generatedUsername = `${truncatedPrefix}${randomSuffix}`;      const response = await api.Signup({diff --git a/frontend/apps/artcraft-webapp/src/components/prompt-box/ImagePromptRow.tsx b/frontend/apps/artcraft-webapp/src/components/prompt-box/ImagePromptRow.tsx--- a/frontend/apps/artcraft-webapp/src/components/prompt-box/ImagePromptRow.tsx+++ b/frontend/apps/artcraft-webapp/src/components/prompt-box/ImagePromptRow.tsx@@ -136,7 +136,7 @@ export const ImagePromptRow = ({     const filesToProcess = files.slice(0, availableSlots);      filesToProcess.forEach(async (file) => {-      const uploadId = Math.random().toString(36).substring(7);+      const uploadId = Math.random().toString(36).substring(7); // NOSONAR: UI-only id or cosmetic value, never a secret.       setUploadingImages((prev) => [...prev, { id: uploadId, previewUrl: "" }]);        // Downscaled preview; the committed thumbnail reuses it while the@@ -150,7 +150,7 @@ export const ImagePromptRow = ({         setUploadingImages((prev) => prev.filter((img) => img.id !== uploadId));        await uploadImage({-        title: `reference-image-${Math.random().toString(36).substring(2, 15)}`,+        title: `reference-image-${Math.random().toString(36).substring(2, 15)}`, // NOSONAR: UI-only id or cosmetic value, never a secret.         assetFile: file,         progressCallback: (newState) => {           if (newState.status === UploaderStates.success && newState.data) {@@ -196,12 +196,12 @@ export const ImagePromptRow = ({     setUploadingEndFrame(true);     const previewUrl = await createImagePreviewUrl(file);     await uploadImage({-      title: `end-frame-${Math.random().toString(36).substring(2, 15)}`,+      title: `end-frame-${Math.random().toString(36).substring(2, 15)}`, // NOSONAR: UI-only id or cosmetic value, never a secret.       assetFile: file,       progressCallback: (newState) => {         if (newState.status === UploaderStates.success && newState.data) {           setEndFrameImage({-            id: Math.random().toString(36).substring(7),+            id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.             url: previewUrl,             fullUrl: URL.createObjectURL(file),             file,diff --git a/frontend/apps/artcraft-webapp/src/components/prompt-box/MediaReferenceRow.tsx b/frontend/apps/artcraft-webapp/src/components/prompt-box/MediaReferenceRow.tsx--- a/frontend/apps/artcraft-webapp/src/components/prompt-box/MediaReferenceRow.tsx+++ b/frontend/apps/artcraft-webapp/src/components/prompt-box/MediaReferenceRow.tsx@@ -114,12 +114,12 @@ export const MediaReferenceRow = ({     setUploadingVideo(true);     try {       await uploadVideo({-        title: `reference-video-${Math.random().toString(36).substring(2, 15)}`,+        title: `reference-video-${Math.random().toString(36).substring(2, 15)}`, // NOSONAR: UI-only id or cosmetic value, never a secret.         assetFile: file,         progressCallback: (newState) => {           if (newState.status === UploaderStates.success && newState.data) {             const refVideo: RefVideo = {-              id: Math.random().toString(36).substring(7),+              id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.               url: URL.createObjectURL(file),               file,               mediaToken: newState.data,@@ -172,12 +172,12 @@ export const MediaReferenceRow = ({     setUploadingAudio(true);     try {       await uploadAudio({-        title: `reference-audio-${Math.random().toString(36).substring(2, 15)}`,+        title: `reference-audio-${Math.random().toString(36).substring(2, 15)}`, // NOSONAR: UI-only id or cosmetic value, never a secret.         assetFile: file,         progressCallback: (newState) => {           if (newState.status === UploaderStates.success && newState.data) {             const refAudio: RefAudio = {-              id: Math.random().toString(36).substring(7),+              id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.               url: URL.createObjectURL(file),               file,               mediaToken: newState.data,diff --git a/frontend/apps/artcraft-webapp/src/pages/create-audio/create-audio.tsx b/frontend/apps/artcraft-webapp/src/pages/create-audio/create-audio.tsx--- a/frontend/apps/artcraft-webapp/src/pages/create-audio/create-audio.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/create-audio/create-audio.tsx@@ -351,7 +351,7 @@ export default function CreateAudio() {         }         total += duration;         added.push({-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url,           file: new File([], "library-audio"),           mediaToken: item.id,@@ -370,7 +370,7 @@ export default function CreateAudio() {       const newImages: RefImage[] = items         .slice(0, maxImageRefs)         .map((item) => ({-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url: item.thumbnail || item.fullImage || "",           fullUrl: item.fullImage || undefined,           file: new File([], "library-image"),diff --git a/frontend/apps/artcraft-webapp/src/pages/create-object/components/mesh-upload.ts b/frontend/apps/artcraft-webapp/src/pages/create-object/components/mesh-upload.ts--- a/frontend/apps/artcraft-webapp/src/pages/create-object/components/mesh-upload.ts+++ b/frontend/apps/artcraft-webapp/src/pages/create-object/components/mesh-upload.ts@@ -8,7 +8,7 @@ import type { RefImage } from "../../../components/prompt-box";  export const MESH_FILE_ACCEPT = ".glb,.gltf,.fbx,.obj"; -const randomId = () => Math.random().toString(36).substring(7);+const randomId = () => Math.random().toString(36).substring(7); // NOSONAR: UI-only id or cosmetic value, never a secret.  const readAsDataUrl = (file: File): Promise<string> =>   new Promise((resolve, reject) => {@@ -26,7 +26,7 @@ export async function uploadViewImage(   const url = await readAsDataUrl(file);   return new Promise((resolve) => {     uploadImage({-      title: `${label.toLowerCase()}-view-${Math.random().toString(36).substring(2, 10)}`,+      title: `${label.toLowerCase()}-view-${Math.random().toString(36).substring(2, 10)}`, // NOSONAR: UI-only id or cosmetic value, never a secret.       assetFile: file,       progressCallback: (state) => {         if (state.status === UploaderStates.success && state.data) {diff --git a/frontend/apps/artcraft-webapp/src/pages/create-object/create-object.tsx b/frontend/apps/artcraft-webapp/src/pages/create-object/create-object.tsx--- a/frontend/apps/artcraft-webapp/src/pages/create-object/create-object.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/create-object/create-object.tsx@@ -318,7 +318,7 @@ export default function CreateObject() {       const item = items[0];       if (item && libraryTarget) {         const refImage: RefImage = {-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url: item.thumbnail || item.fullImage || "",           fullUrl: item.fullImage || undefined,           file: new File([], "library-image"),diff --git a/frontend/apps/artcraft-webapp/src/pages/create-vfx/create-vfx.tsx b/frontend/apps/artcraft-webapp/src/pages/create-vfx/create-vfx.tsx--- a/frontend/apps/artcraft-webapp/src/pages/create-vfx/create-vfx.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/create-vfx/create-vfx.tsx@@ -135,7 +135,7 @@ export default function CreateVFX() {           const id =             typeof crypto !== "undefined" && crypto.randomUUID               ? crypto.randomUUID()-              : Math.random().toString(36).slice(2);+              : Math.random().toString(36).slice(2); // NOSONAR: UI-only id or cosmetic value, never a secret.           seedFromSession({             id,             status: job.status,diff --git a/frontend/apps/artcraft-webapp/src/pages/create-video/create-video.tsx b/frontend/apps/artcraft-webapp/src/pages/create-video/create-video.tsx--- a/frontend/apps/artcraft-webapp/src/pages/create-video/create-video.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/create-video/create-video.tsx@@ -1093,7 +1093,7 @@ export default function CreateVideo() {       const newImages: RefImage[] = items         .slice(0, availableSlots)         .map((item) => ({-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url: item.thumbnail || item.fullImage || "",           fullUrl: item.fullImage || undefined,           file: new File([], "library-image"),@@ -1126,7 +1126,7 @@ export default function CreateVideo() {       const candidates: RefVideo[] = items         .filter((item) => !!item.fullImage)         .map((item) => ({-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url: item.fullImage!,           file: new File([], "library-video"),           mediaToken: item.id,@@ -1190,7 +1190,7 @@ export default function CreateVideo() {         }         total += duration;         added.push({-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url,           file: new File([], "library-audio"),           mediaToken: item.id,@@ -1213,7 +1213,7 @@ export default function CreateVideo() {     const item = items[0];     if (!item) return;     setEndFrameImage({-      id: Math.random().toString(36).substring(7),+      id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.       url: item.thumbnail || item.fullImage || "",       fullUrl: item.fullImage || undefined,       file: new File([], "library-image"),diff --git a/frontend/apps/artcraft-webapp/src/pages/create-world/components/ReferenceVideoSlot.tsx b/frontend/apps/artcraft-webapp/src/pages/create-world/components/ReferenceVideoSlot.tsx--- a/frontend/apps/artcraft-webapp/src/pages/create-world/components/ReferenceVideoSlot.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/create-world/components/ReferenceVideoSlot.tsx@@ -35,7 +35,7 @@ export function ReferenceVideoSlot({       progressCallback: (state) => {         if (state.status === UploaderStates.success && state.data) {           onChange({-            id: Math.random().toString(36).substring(7),+            id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.             url: previewUrl,             file,             mediaToken: state.data,diff --git a/frontend/apps/artcraft-webapp/src/pages/create-world/create-world.tsx b/frontend/apps/artcraft-webapp/src/pages/create-world/create-world.tsx--- a/frontend/apps/artcraft-webapp/src/pages/create-world/create-world.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/create-world/create-world.tsx@@ -271,7 +271,7 @@ export default function CreateWorld() {       const newImages: RefImage[] = items         .slice(0, availableSlots)         .map((item) => ({-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url: item.thumbnail || item.fullImage || "",           fullUrl: item.fullImage || undefined,           file: new File([], "library-image"),diff --git a/frontend/apps/artcraft-webapp/src/pages/pagedraw/base-image-selector.tsx b/frontend/apps/artcraft-webapp/src/pages/pagedraw/base-image-selector.tsx--- a/frontend/apps/artcraft-webapp/src/pages/pagedraw/base-image-selector.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/pagedraw/base-image-selector.tsx@@ -64,11 +64,10 @@ export function BaseImageSelector({    const handleBlankCanvasConfirm = (width: number, height: number) => {     setIsBlankCanvasOpen(false);+    const suffix = Math.random().toString(36).substring(2, 8); // NOSONAR: UI-only id or cosmetic value, never a secret.     onImageSelect({       url: "",-      mediaToken: `blank_canvas_${width}x${height}_${Math.random()-        .toString(36)-        .substring(2, 8)}`,+      mediaToken: `blank_canvas_${width}x${height}_${suffix}`,       isBlankCanvas: true,       blankCanvasWidth: width,       blankCanvasHeight: height,@@ -81,7 +80,7 @@ export function BaseImageSelector({     reader.onloadend = async () => {       const dataUrl = reader.result as string;       await uploadImage({-        title: `edit-image-${Math.random().toString(36).substring(2, 15)}`,+        title: `edit-image-${Math.random().toString(36).substring(2, 15)}`, // NOSONAR: UI-only id or cosmetic value, never a secret.         assetFile: file,         progressCallback: (state) => {           if (state.status === UploaderStates.success && state.data) {diff --git a/frontend/apps/artcraft-website-next/src/components/campaign/auth-gate-modal.tsx b/frontend/apps/artcraft-website-next/src/components/campaign/auth-gate-modal.tsx--- a/frontend/apps/artcraft-website-next/src/components/campaign/auth-gate-modal.tsx+++ b/frontend/apps/artcraft-website-next/src/components/campaign/auth-gate-modal.tsx@@ -107,7 +107,7 @@ function SignupForm({     // Username generated from the email, same scheme as the Vite form:     // up to 7 alphanumerics from the local part plus a 3-digit suffix.     const prefix = email.split("@")[0].replace(/[^a-zA-Z0-9]/g, "").slice(0, 7);-    const username = `${prefix}${Math.floor(Math.random() * 900) + 100}`;+    const username = `${prefix}${Math.floor(Math.random() * 900) + 100}`; // NOSONAR: UI-only id or cosmetic value, never a secret.     const result = await signup({       username,       email,diff --git a/frontend/apps/genhub/src/hooks/use-auth.tsx b/frontend/apps/genhub/src/hooks/use-auth.tsx--- a/frontend/apps/genhub/src/hooks/use-auth.tsx+++ b/frontend/apps/genhub/src/hooks/use-auth.tsx@@ -73,7 +73,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {    const signup = useCallback(     async (email: string, password: string): Promise<string | null> => {-      const username = email.split('@')[0] + Math.floor(Math.random() * 1000);+      const username = email.split('@')[0] + Math.floor(Math.random() * 1000); // NOSONAR: UI-only id or cosmetic value, never a secret.       const res = await api.Signup({         username,         email,diff --git a/frontend/libs/components/image-input/src/lib/image-input.tsx b/frontend/libs/components/image-input/src/lib/image-input.tsx--- a/frontend/libs/components/image-input/src/lib/image-input.tsx+++ b/frontend/libs/components/image-input/src/lib/image-input.tsx@@ -47,7 +47,7 @@ export function ImageInput({     const reader = new FileReader();     reader.onloadend = () => {       const newImage: ImageFile = {-        id: Math.random().toString(36).substring(7),+        id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.         url: reader.result as string,         file: file,       };diff --git a/frontend/libs/components/modal/src/lib/modal.tsx b/frontend/libs/components/modal/src/lib/modal.tsx--- a/frontend/libs/components/modal/src/lib/modal.tsx+++ b/frontend/libs/components/modal/src/lib/modal.tsx@@ -927,7 +927,7 @@ export const Modal = ({   }, [allowBackgroundInteraction]);    // Register this modal in the global registry for ESC handling-  const idRef = useRef<number>(Math.floor(Math.random() * 1e9));+  const idRef = useRef<number>(Math.floor(Math.random() * 1e9)); // NOSONAR: UI-only id or cosmetic value, never a secret.    useEffect(() => {     if (!isOpen) return;diff --git a/frontend/libs/components/pagedraw/src/lib/HistoryStack.tsx b/frontend/libs/components/pagedraw/src/lib/HistoryStack.tsx--- a/frontend/libs/components/pagedraw/src/lib/HistoryStack.tsx+++ b/frontend/libs/components/pagedraw/src/lib/HistoryStack.tsx@@ -53,7 +53,7 @@ export const HistoryStack = ({    // This is used to force image reloads in different sessions   // and prevent fetching CORS-tainted images from cache-  const sessionRandBuster = useRef(Math.random());+  const sessionRandBuster = useRef(Math.random()); // NOSONAR: UI-only id or cosmetic value, never a secret.   const scrollRef = useRef<HTMLDivElement | null>(null);   const prevPendingCountRef = useRef<number>(0); diff --git a/frontend/libs/components/pagedraw/src/lib/PageDraw.tsx b/frontend/libs/components/pagedraw/src/lib/PageDraw.tsx--- a/frontend/libs/components/pagedraw/src/lib/PageDraw.tsx+++ b/frontend/libs/components/pagedraw/src/lib/PageDraw.tsx@@ -857,7 +857,7 @@ const PageDraw = ({          const subscriberId: string =           crypto?.randomUUID?.() ??-          `inpaint-${Date.now()}-${Math.random().toString(36).slice(2)}`;+          `inpaint-${Date.now()}-${Math.random().toString(36).slice(2)}`; // NOSONAR: UI-only id or cosmetic value, never a secret.          adapter.onEnqueueMeta?.({           prompt,diff --git a/frontend/libs/components/pagedraw/src/lib/PaintSurface.tsx b/frontend/libs/components/pagedraw/src/lib/PaintSurface.tsx--- a/frontend/libs/components/pagedraw/src/lib/PaintSurface.tsx+++ b/frontend/libs/components/pagedraw/src/lib/PaintSurface.tsx@@ -356,7 +356,7 @@ export const PaintSurface = ({         activeTool === "inpaint") &&       isWithinLeftPanel(stagePoint)     ) {-      const lineId = `line-${activeTool}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;+      const lineId = `line-${activeTool}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; // NOSONAR: UI-only id or cosmetic value, never a secret.       const opacity = activeTool === "draw" ? brushOpacity : 1;        const composite =@@ -445,7 +445,7 @@ export const PaintSurface = ({       isWithinLeftPanel(stagePoint) &&       e.target === e.target.getStage()     ) {-      const id = `node-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;+      const id = `node-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; // NOSONAR: UI-only id or cosmetic value, never a secret.       const commonProps = {         id,         x: stagePoint.x,diff --git a/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts b/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts--- a/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts+++ b/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts@@ -322,7 +322,7 @@ export interface SceneState { }  export const generateId = (): string => {-  return Math.random().toString(36).substring(2, 9);+  return Math.random().toString(36).substring(2, 9); // NOSONAR: UI-only id or cosmetic value, never a secret. };  // Add a flag to prevent saving state during restorationdiff --git a/frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx b/frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx--- a/frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx+++ b/frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx@@ -340,9 +340,9 @@ export const Stage3DBody = ({     }     const newIndex = cameras.length + 1;     const newId = `cam${newIndex}`;-    const radius = Math.random() * 5 + 7;-    const theta = Math.random() * Math.PI * 2;-    const phi = Math.PI / 3 + (Math.random() * Math.PI) / 6;+    const radius = Math.random() * 5 + 7; // NOSONAR: UI-only id or cosmetic value, never a secret.+    const theta = Math.random() * Math.PI * 2; // NOSONAR: UI-only id or cosmetic value, never a secret.+    const phi = Math.PI / 3 + (Math.random() * Math.PI) / 6; // NOSONAR: UI-only id or cosmetic value, never a secret.     const randomX = radius * Math.sin(phi) * Math.cos(theta);     const randomY = Math.abs(radius * Math.cos(phi)) + 2;     const randomZ = radius * Math.sin(phi) * Math.sin(theta);diff --git a/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx b/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx--- a/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx+++ b/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx@@ -448,19 +448,18 @@ export const ImagePromptRow = ({         break;       } -      const uploadId = Math.random().toString(36).substring(7);+      const uploadId = Math.random().toString(36).substring(7); // NOSONAR: UI-only id or cosmetic value, never a secret.       setUploadingVideo({ id: uploadId, file });        if (uploadVideo) {+        const titleSuffix = Math.random().toString(36).substring(2, 15); // NOSONAR: UI-only id or cosmetic value, never a secret.         await uploadVideo({-          title: `reference-video-${Math.random()-            .toString(36)-            .substring(2, 15)}`,+          title: `reference-video-${titleSuffix}`,           assetFile: file,           progressCallback: (newState) => {             if (newState.status === UploaderStates.success && newState.data) {               const refVideo: RefVideo = {-                id: Math.random().toString(36).substring(7),+                id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.                 url: URL.createObjectURL(file),                 file,                 mediaToken: newState.data,@@ -480,7 +479,7 @@ export const ImagePromptRow = ({         });       } else {         const refVideo: RefVideo = {-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url: URL.createObjectURL(file),           file,           mediaToken: "",@@ -562,19 +561,18 @@ export const ImagePromptRow = ({         break;       } -      const uploadId = Math.random().toString(36).substring(7);+      const uploadId = Math.random().toString(36).substring(7); // NOSONAR: UI-only id or cosmetic value, never a secret.       setUploadingAudio({ id: uploadId, file });        if (uploadAudio) {+        const titleSuffix = Math.random().toString(36).substring(2, 15); // NOSONAR: UI-only id or cosmetic value, never a secret.         await uploadAudio({-          title: `reference-audio-${Math.random()-            .toString(36)-            .substring(2, 15)}`,+          title: `reference-audio-${titleSuffix}`,           assetFile: file,           progressCallback: (newState) => {             if (newState.status === UploaderStates.success && newState.data) {               const refAudio: RefAudio = {-                id: Math.random().toString(36).substring(7),+                id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.                 url: URL.createObjectURL(file),                 file,                 mediaToken: newState.data,@@ -592,7 +590,7 @@ export const ImagePromptRow = ({         });       } else {         const refAudio: RefAudio = {-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url: URL.createObjectURL(file),           file,           mediaToken: "",@@ -629,7 +627,7 @@ export const ImagePromptRow = ({         : files.slice(0, availableSlots);      filesToProcess.forEach(async (file) => {-      const uploadId = Math.random().toString(36).substring(7);+      const uploadId = Math.random().toString(36).substring(7); // NOSONAR: UI-only id or cosmetic value, never a secret.       if (uploadTarget === "end") {         setUploadingEnd({ id: uploadId, file, previewUrl: "" });       } else {@@ -680,10 +678,9 @@ export const ImagePromptRow = ({       };        if (uploadImage) {+        const titleSuffix = Math.random().toString(36).substring(2, 15); // NOSONAR: UI-only id or cosmetic value, never a secret.         await uploadImage({-          title: `reference-image-${Math.random()-            .toString(36)-            .substring(2, 15)}`,+          title: `reference-image-${titleSuffix}`,           assetFile: file,           progressCallback: (newState) => {             if (newState.status === UploaderStates.success && newState.data) {@@ -767,7 +764,7 @@ export const ImagePromptRow = ({           }           currentTotal += duration;           newVideos.push({-            id: Math.random().toString(36).substring(7),+            id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.             url: item.fullImage,             file: new File([], "library-video"),             mediaToken: item.id,@@ -794,7 +791,7 @@ export const ImagePromptRow = ({       const item = selectedItems[0];       if (item && item.fullImage) {         setEndFrameImage?.({-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url: item.fullImage,           file: new File([], "library-image"),           mediaToken: item.id,@@ -818,7 +815,7 @@ export const ImagePromptRow = ({     selectedItems.slice(0, availableSlots).forEach((item) => {       if (!item.fullImage) return;       newRefs.push({-        id: Math.random().toString(36).substring(7),+        id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.         url: item.fullImage,         file: new File([], "library-image"),         mediaToken: item.id,diff --git a/frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx b/frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx--- a/frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx+++ b/frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx@@ -273,7 +273,7 @@ export const PromptBoxAudio = ({         }         total += duration;         added.push({-          id: Math.random().toString(36).substring(7),+          id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.           url: item.fullImage,           file: new File([], "library-audio"),           mediaToken: item.id,diff --git a/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx b/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx--- a/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx+++ b/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx@@ -86,7 +86,7 @@ export const PromptBoxImage = ({   useEffect(() => {     if (imageMediaId && url) {       const referenceImage: RefImage = {-        id: Math.random().toString(36).substring(7),+        id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.         url: url,         file: new File([], "library-image"),         mediaToken: imageMediaId,@@ -269,7 +269,7 @@ export const PromptBoxImage = ({   useEffect(() => {     if (imageMediaId && url) {       const referenceImage: RefImage = {-        id: Math.random().toString(36).substring(7),+        id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.         url: url,         file: new File([], "library-image"),         mediaToken: imageMediaId,@@ -371,7 +371,7 @@ export const PromptBoxImage = ({      const subscriberId = crypto.randomUUID       ? crypto.randomUUID()-      : Math.random().toString(36).slice(2);+      : Math.random().toString(36).slice(2); // NOSONAR: UI-only id or cosmetic value, never a secret.      setTimeout(() => {       console.debug("Turn off blocking of prompt box...");diff --git a/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx b/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx--- a/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx+++ b/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx@@ -121,7 +121,7 @@ export const PromptBoxVideo = ({   useEffect(() => {     if (imageMediaId && url) {       const referenceImage: RefImage = {-        id: Math.random().toString(36).substring(7),+        id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.         url: url,         file: new File([], "library-image"),         mediaToken: imageMediaId,@@ -324,7 +324,7 @@ export const PromptBoxVideo = ({   useEffect(() => {     if (imageMediaId && url) {       const referenceImage: RefImage = {-        id: Math.random().toString(36).substring(7),+        id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.         url: url,         file: new File([], "library-image"),         mediaToken: imageMediaId,@@ -1199,7 +1199,7 @@ export const PromptBoxVideo = ({     for (let i = 0; i < count; i++) {       const subscriberId = crypto.randomUUID         ? crypto.randomUUID()-        : Math.random().toString(36).slice(2);+        : Math.random().toString(36).slice(2); // NOSONAR: UI-only id or cosmetic value, never a secret.       subscriberIds.push(subscriberId);       enqueuePromises.push(GenerateVideo(buildRequest(subscriberId)));     }diff --git a/frontend/libs/components/promptbox/src/lib/common/AudioReferenceRow.tsx b/frontend/libs/components/promptbox/src/lib/common/AudioReferenceRow.tsx--- a/frontend/libs/components/promptbox/src/lib/common/AudioReferenceRow.tsx+++ b/frontend/libs/components/promptbox/src/lib/common/AudioReferenceRow.tsx@@ -104,12 +104,12 @@ export const AudioReferenceRow = forwardRef<       if (uploadAudio) {         setIsUploadingAudio(true);         await uploadAudio({-          title: `reference-audio-${Math.random().toString(36).substring(2, 15)}`,+          title: `reference-audio-${Math.random().toString(36).substring(2, 15)}`, // NOSONAR: UI-only id or cosmetic value, never a secret.           assetFile: file,           progressCallback: (newState) => {             if (newState.status === UploaderStates.success && newState.data) {               const refAudio: RefAudio = {-                id: Math.random().toString(36).substring(7),+                id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.                 url: URL.createObjectURL(file),                 file,                 mediaToken: newState.data,@@ -145,12 +145,12 @@ export const AudioReferenceRow = forwardRef<      setIsUploadingImage(true);     await uploadImage({-      title: `reference-image-${Math.random().toString(36).substring(2, 15)}`,+      title: `reference-image-${Math.random().toString(36).substring(2, 15)}`, // NOSONAR: UI-only id or cosmetic value, never a secret.       assetFile: file,       progressCallback: (newState) => {         if (newState.status === UploaderStates.success && newState.data) {           const refImage: RefImage = {-            id: Math.random().toString(36).substring(7),+            id: Math.random().toString(36).substring(7), // NOSONAR: UI-only id or cosmetic value, never a secret.             url: URL.createObjectURL(file),             file,             mediaToken: newState.data,diff --git a/frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx b/frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx--- a/frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx+++ b/frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx@@ -65,7 +65,7 @@ export interface UseDeckMediaOptions<   ownGalleryModal?: boolean; } -const randomId = () => Math.random().toString(36).substring(7);+const randomId = () => Math.random().toString(36).substring(7); // NOSONAR: UI-only id or cosmetic value, never a secret.  // A non-finite duration cap means "no limit" — leave it out of the message. const videoLimitMessage = (maxVideos: number, maxTotalSec: number) =>@@ -74,7 +74,7 @@ const videoLimitMessage = (maxVideos: number, maxTotalSec: number) =>     : `Max ${maxVideos} video${maxVideos === 1 ? "" : "s"}`;  const randomTitle = (prefix: string) =>-  `${prefix}-${Math.random().toString(36).substring(2, 15)}`;+  `${prefix}-${Math.random().toString(36).substring(2, 15)}`; // NOSONAR: UI-only id or cosmetic value, never a secret.  const getVideoDurationFromSrc = (src: string): Promise<number> =>   new Promise((resolve) => {diff --git a/frontend/libs/components/vfx/src/lib/PromptBoxVFX.tsx b/frontend/libs/components/vfx/src/lib/PromptBoxVFX.tsx--- a/frontend/libs/components/vfx/src/lib/PromptBoxVFX.tsx+++ b/frontend/libs/components/vfx/src/lib/PromptBoxVFX.tsx@@ -95,12 +95,12 @@ export const PromptBoxVFX = ({        setSourceUploading(true);       await uploadVideo({-        title: `vfx-source-${Math.random().toString(36).slice(2, 10)}`,+        title: `vfx-source-${Math.random().toString(36).slice(2, 10)}`, // NOSONAR: UI-only id or cosmetic value, never a secret.         assetFile: file,         progressCallback: (state) => {           if (state.status === UploaderStates.success && state.data) {             setSource({-              id: Math.random().toString(36).slice(2),+              id: Math.random().toString(36).slice(2), // NOSONAR: UI-only id or cosmetic value, never a secret.               url: previewUrl,               mediaToken: state.data,             });@@ -136,12 +136,12 @@ export const PromptBoxVFX = ({        setUploading(true);       await uploadImage({-        title: `vfx-${kind}-${Math.random().toString(36).slice(2, 10)}`,+        title: `vfx-${kind}-${Math.random().toString(36).slice(2, 10)}`, // NOSONAR: UI-only id or cosmetic value, never a secret.         assetFile: file,         progressCallback: (state) => {           if (state.status === UploaderStates.success && state.data) {             setRef({-              id: Math.random().toString(36).slice(2),+              id: Math.random().toString(36).slice(2), // NOSONAR: UI-only id or cosmetic value, never a secret.               url: previewUrl,               mediaToken: state.data,             });@@ -183,7 +183,7 @@ export const PromptBoxVFX = ({         return;       }       const ref = {-        id: Math.random().toString(36).slice(2),+        id: Math.random().toString(36).slice(2), // NOSONAR: UI-only id or cosmetic value, never a secret.         url: item.fullImage || item.thumbnail || "",         mediaToken: item.id,       };diff --git a/frontend/libs/components/vfx/src/lib/api.ts b/frontend/libs/components/vfx/src/lib/api.ts--- a/frontend/libs/components/vfx/src/lib/api.ts+++ b/frontend/libs/components/vfx/src/lib/api.ts@@ -67,5 +67,5 @@ export function newIdempotencyToken(): string {   if (typeof crypto !== "undefined" && crypto.randomUUID) {     return crypto.randomUUID();   }-  return Math.random().toString(36).slice(2) + Date.now().toString(36);+  return Math.random().toString(36).slice(2) + Date.now().toString(36); // NOSONAR: fallback idempotency token, a uniqueness key and not a secret. }diff --git a/frontend/libs/components/vfx/src/lib/store.ts b/frontend/libs/components/vfx/src/lib/store.ts--- a/frontend/libs/components/vfx/src/lib/store.ts+++ b/frontend/libs/components/vfx/src/lib/store.ts@@ -93,7 +93,7 @@ export const useVFXStore = create<VFXState>()(         const id =           typeof crypto !== "undefined" && crypto.randomUUID             ? crypto.randomUUID()-            : Math.random().toString(36).slice(2);+            : Math.random().toString(36).slice(2); // NOSONAR: UI-only id or cosmetic value, never a secret.         const result: VFXResult = {           id,           status: "pending",diff --git a/frontend/libs/components/video-editor/src/lib/adapters/default/indexeddb-project-storage.ts b/frontend/libs/components/video-editor/src/lib/adapters/default/indexeddb-project-storage.ts--- a/frontend/libs/components/video-editor/src/lib/adapters/default/indexeddb-project-storage.ts+++ b/frontend/libs/components/video-editor/src/lib/adapters/default/indexeddb-project-storage.ts@@ -50,7 +50,7 @@ function tx<T>( function newId(): string {   // Sufficient for local-only storage; the lib does not expose this   // id outside the host process.-  return `proj_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`;+  return `proj_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`; // NOSONAR: UI-only id or cosmetic value, never a secret. }  export function createIndexedDBProjectStorage(): ProjectStorageAdapter {diff --git a/frontend/libs/components/video-editor/src/lib/adapters/default/local-file-media-source.ts b/frontend/libs/components/video-editor/src/lib/adapters/default/local-file-media-source.ts--- a/frontend/libs/components/video-editor/src/lib/adapters/default/local-file-media-source.ts+++ b/frontend/libs/components/video-editor/src/lib/adapters/default/local-file-media-source.ts@@ -17,7 +17,7 @@ interface Entry { }  function newId(): string {-  return `media_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`;+  return `media_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`; // NOSONAR: UI-only id or cosmetic value, never a secret. }  export function createLocalFileMediaSource(): MediaSourceAdapter {