Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,12 @@ function _getReferencedDisplaySetMetadata(referencedDisplaySet, segDisplaySet) {

const { PixelMeasuresSequence } = SharedFunctionalGroup;

const PixelMeasures = Array.isArray(PixelMeasuresSequence)
? PixelMeasuresSequence[0]
: PixelMeasuresSequence;
const PixelMeasures =
(Array.isArray(PixelMeasuresSequence) ? PixelMeasuresSequence[0] : PixelMeasuresSequence) || {};

if (!PixelMeasuresSequence || Object.keys(PixelMeasures).length === 0) {
console.warn('PixelMeasuresSequence missing from SEG instance metadata.');
}
const { SpacingBetweenSlices, SliceThickness } = PixelMeasures;

const image0 = referencedDisplaySet.images[0];
Expand Down
8 changes: 7 additions & 1 deletion platform/core/src/classes/MetadataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,13 @@ class MetadataProvider {
return;
}

return (frameNumber && combineFrameInstance(frameNumber, instance)) || instance;
const combined = frameNumber && combineFrameInstance(frameNumber, instance);
if (combined) {
// Add imageId to multiframe result so it matches single-frame instance.
combined.imageId = imageId;
return combined;
}
Comment on lines +63 to +68
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Mutation of a cached combined instance

createCombinedValue inside combineFrameInstance caches the per-frame object at instance._parentInstance[frameNumber] and returns the same reference on every subsequent call. Setting combined.imageId = imageId permanently stamps that imageId onto the cached object. If the same SOP-instance frame is ever resolved through two different imageIds (e.g., the same DICOM served from two WADO-RS endpoints), the second caller receives a combined instance whose imageId belongs to the first caller.

Additionally, when NumberOfFrames < 2 combineFrameInstance returns the original instance reference unchanged. If frameNumber is somehow truthy for such an instance, combined === instance, and combined.imageId = imageId mutates the shared object stored in DicomMetadataStore directly — not just its per-frame projection.

Prompt To Fix With AI
This is a comment left during a code review.
Path: platform/core/src/classes/MetadataProvider.ts
Line: 63-68

Comment:
**Mutation of a cached combined instance**

`createCombinedValue` inside `combineFrameInstance` caches the per-frame object at `instance._parentInstance[frameNumber]` and returns the same reference on every subsequent call. Setting `combined.imageId = imageId` permanently stamps that `imageId` onto the cached object. If the same SOP-instance frame is ever resolved through two different imageIds (e.g., the same DICOM served from two WADO-RS endpoints), the second caller receives a combined instance whose `imageId` belongs to the first caller.

Additionally, when `NumberOfFrames < 2` `combineFrameInstance` returns the original `instance` reference unchanged. If `frameNumber` is somehow truthy for such an instance, `combined === instance`, and `combined.imageId = imageId` mutates the shared object stored in `DicomMetadataStore` directly — not just its per-frame projection.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by avoiding mutation of cached/shared metadata objects in combineFrameInstance.

Previously, combined.imageId = imageId modified shared references returned from createCombinedValue, which could leak imageId values across callers. For single-frame instances, it could also mutate the original metadata object stored in DicomMetadataStore.

Updated the implementation to return a cloned object before assigning imageId.

return instance;
}

get(query, imageId, options = { fallback: false }) {
Expand Down
Loading