@@ -15,6 +15,29 @@ import {convertUndefined, deepCopy, get, reshape, throttle} from "./util"
1515
1616import plotly_css from "styles/models/plotly.css"
1717
18+ const FORBIDDEN_KEYS = new Set ( [ "__proto__" , "prototype" , "constructor" ] )
19+
20+ function isRecord ( value : unknown ) : value is Record < string , unknown > {
21+ return typeof value === "object" && value !== null
22+ }
23+
24+ function isSafePath ( path : string [ ] ) : boolean {
25+ return path . every ( ( part ) => part . length > 0 && ! FORBIDDEN_KEYS . has ( part ) )
26+ }
27+
28+ function getSafeParent ( obj : unknown , path : string [ ] ) : Record < string , unknown > | null {
29+ let current : unknown = obj
30+
31+ for ( const key of path ) {
32+ if ( ! isRecord ( current ) || FORBIDDEN_KEYS . has ( key ) || ! Object . hasOwn ( current , key ) ) {
33+ return null
34+ }
35+ current = current [ key ]
36+ }
37+
38+ return isRecord ( current ) ? current : null
39+ }
40+
1841export class PlotlyEvent extends ModelEvent {
1942 constructor ( readonly data : any ) {
2043 super ( )
@@ -422,17 +445,21 @@ export class PlotlyPlotView extends HTMLBoxView {
422445 if ( array . shape != null && array . shape . length > 1 ) {
423446 array = reshape ( array , array . shape )
424447 }
425- const prop_path = column . split ( "." )
426- const prop = prop_path [ prop_path . length - 1 ]
427- let prop_parent = trace
428- for ( const k of prop_path . slice ( 0 , - 1 ) ) {
429- prop_parent = ( prop_parent [ k ] )
448+
449+ // Column name used for resolving object data needs to be validated
450+ // to avoid prototype pollution
451+ const propPath = column . split ( "." )
452+ const prop = propPath [ propPath . length - 1 ]
453+ const propParent = getSafeParent ( trace , propPath . slice ( 0 , - 1 ) )
454+ if ( ! propParent || ! prop || ! isSafePath ( propPath ) || FORBIDDEN_KEYS . has ( prop ) ) {
455+ console . warn ( "Attempted prototype pollution detected via Plotly column resolution." )
456+ continue
430457 }
431458
432- if ( update && prop_path . length == 1 ) {
433- prop_parent [ prop ] = [ array ]
459+ if ( update && propPath . length == 1 ) {
460+ propParent [ prop ] = [ array ]
434461 } else {
435- prop_parent [ prop ] = array
462+ propParent [ prop ] = array
436463 }
437464 }
438465 return trace
0 commit comments