Skip to content
Open
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
38 changes: 21 additions & 17 deletions web/default/src/components/json-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ type EditorRow = {
value: string
}

function parseJsonRows(json: string): EditorRow[] | null {
try {
if (!json.trim()) {
return []
}

const parsed = JSON.parse(json)
return Object.entries(parsed).map(([key, val], index) => ({
id: `${Date.now()}-${index}`,
key,
value: typeof val === 'object' ? JSON.stringify(val) : String(val),
}))
} catch (_error) {
return null
}
}
Comment on lines +46 to +61
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add type validation to ensure parsed JSON is a plain object.

The function calls Object.entries(parsed) without checking whether parsed is actually an object. Non-object JSON values will produce incorrect results:

  • Arrays like [1, 2, 3] become rows with numeric string keys ("0", "1", "2")
  • Primitives like 123 or true become empty rows (data loss)
  • Strings like "hello" become character-indexed rows

Since the component is designed for key-value pairs (objects only), add a type guard after parsing to return null for non-object types.

🛡️ Proposed fix to validate object type
 function parseJsonRows(json: string): EditorRow[] | null {
   try {
     if (!json.trim()) {
       return []
     }
 
     const parsed = JSON.parse(json)
+    if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
+      return null
+    }
+    
     return Object.entries(parsed).map(([key, val], index) => ({
       id: `${Date.now()}-${index}`,
       key,
       value: typeof val === 'object' ? JSON.stringify(val) : String(val),
     }))
   } catch (_error) {
     return null
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function parseJsonRows(json: string): EditorRow[] | null {
try {
if (!json.trim()) {
return []
}
const parsed = JSON.parse(json)
return Object.entries(parsed).map(([key, val], index) => ({
id: `${Date.now()}-${index}`,
key,
value: typeof val === 'object' ? JSON.stringify(val) : String(val),
}))
} catch (_error) {
return null
}
}
function parseJsonRows(json: string): EditorRow[] | null {
try {
if (!json.trim()) {
return []
}
const parsed = JSON.parse(json)
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
return null
}
return Object.entries(parsed).map(([key, val], index) => ({
id: `${Date.now()}-${index}`,
key,
value: typeof val === 'object' ? JSON.stringify(val) : String(val),
}))
} catch (_error) {
return null
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/components/json-editor.tsx` around lines 46 - 61,
parseJsonRows currently calls Object.entries(parsed) without ensuring parsed is
a plain object; update parseJsonRows to validate after JSON.parse that parsed is
an object suitable for key/value rows (i.e., typeof parsed === 'object', parsed
!== null, and !Array.isArray(parsed)) and return null for any non-object JSON
(arrays, primitives, strings). Keep the existing mapping to EditorRow and id
generation when the check passes; ensure the validation occurs before calling
Object.entries so non-object inputs yield null.


export function JsonEditor({
value,
onChange,
Expand All @@ -63,26 +80,13 @@ export function JsonEditor({
const resolvedKeyLabel = keyLabel ?? t('Key')
const resolvedValueLabel = valueLabel ?? t('Value')
const [mode, setMode] = useState<'visual' | 'json'>('visual')
const [rows, setRows] = useState<EditorRow[]>([])
const [rows, setRows] = useState<EditorRow[]>(() => parseJsonRows(value) ?? [])
const [jsonValue, setJsonValue] = useState(value)

const parseJsonToRows = (json: string) => {
try {
if (!json.trim()) {
setRows([])
return
}
const parsed = JSON.parse(json)
const newRows: EditorRow[] = Object.entries(parsed).map(
([key, val], index) => ({
id: `${Date.now()}-${index}`,
key,
value: typeof val === 'object' ? JSON.stringify(val) : String(val),
})
)
setRows(newRows)
} catch (_error) {
// Invalid JSON, keep current rows
const parsedRows = parseJsonRows(json)
if (parsedRows) {
setRows(parsedRows)
}
}

Expand Down