mirror of
https://github.com/reconurge/flowsint.git
synced 2026-07-16 00:52:35 -05:00
fix(import): preserve apostrophes in valid JSON imports
parse_json unconditionally replaced every single quote with a double
quote (`.replace("'", '"')`) before calling json.loads. This corrupted
any valid JSON string value containing an apostrophe, e.g. a person's
surname "Sarah O'Brien" became "Sarah O"Brien", causing the parse to
fail with "Invalid JSON" and the whole import to be rejected.
Parse strict JSON first so well-formed payloads (including apostrophes
in string values) import correctly, and only fall back to the lenient
single-quote replacement for Python-dict-style payloads that are not
valid JSON on their own.
Adds regression tests covering an apostrophe-bearing JSON value and the
retained single-quoted fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -23,8 +23,15 @@ def parse_json(
|
||||
try:
|
||||
file_bytes = file_bytes.lstrip()
|
||||
entities: Dict[str, Entity] = {}
|
||||
my_bytes_value = file_bytes.decode().replace("'", '"')
|
||||
graph = json.loads(my_bytes_value)
|
||||
decoded = file_bytes.decode()
|
||||
# Parse strict JSON first so apostrophes inside string values (e.g.
|
||||
# "Sarah O'Brien") are preserved. Only fall back to the lenient
|
||||
# single-quote replacement for Python-dict-style payloads that are not
|
||||
# valid JSON on their own.
|
||||
try:
|
||||
graph = json.loads(decoded)
|
||||
except json.JSONDecodeError:
|
||||
graph = json.loads(decoded.replace("'", '"'))
|
||||
node_key = next((k for k in VALID_NODES_KEYS if k in graph), None)
|
||||
edge_key = next((k for k in VALID_EDGES_KEYS if k in graph), None)
|
||||
if node_key is None:
|
||||
|
||||
@@ -65,3 +65,40 @@ def test_standard_json_import_without_label():
|
||||
"""Test basic standard JSON import."""
|
||||
results = parse_json(standard_json_without_label, max_preview_rows=100)
|
||||
assert "Username" in results.entities
|
||||
|
||||
|
||||
# Valid JSON whose string values contain apostrophes (very common in OSINT
|
||||
# data, e.g. surnames like "O'Brien"). This is well-formed JSON and must import.
|
||||
json_with_apostrophe = b"""{
|
||||
"nodes": [
|
||||
{"id": "1", "label": "Sarah O'Brien", "type": "individual"},
|
||||
{"id": "2", "label": "Bob", "type": "individual"}
|
||||
],
|
||||
"edges": [
|
||||
{"source": "1", "target": "2", "label": "KNOWS"}
|
||||
]
|
||||
}"""
|
||||
|
||||
|
||||
def test_json_import_preserves_apostrophes_in_string_values():
|
||||
"""Valid JSON with an apostrophe in a value must parse and keep the value.
|
||||
|
||||
Regression: the parser used to blindly replace every single quote with a
|
||||
double quote, which corrupted valid JSON (turning "Sarah O'Brien" into
|
||||
"Sarah O"Brien") and raised "Invalid JSON".
|
||||
"""
|
||||
results = parse_json(json_with_apostrophe, max_preview_rows=100)
|
||||
|
||||
assert "Individual" in results.entities
|
||||
labels = {
|
||||
str(preview.obj.nodeLabel)
|
||||
for preview in results.entities["Individual"].results
|
||||
}
|
||||
assert "Sarah O'Brien" in labels
|
||||
|
||||
|
||||
def test_json_import_still_accepts_single_quoted_payload():
|
||||
"""Python-dict-style payloads (single-quoted) remain supported as fallback."""
|
||||
single_quoted = b"{'nodes': [{'id': '1', 'label': 'Alice', 'type': 'individual'}], 'edges': []}"
|
||||
results = parse_json(single_quoted, max_preview_rows=100)
|
||||
assert "Individual" in results.entities
|
||||
|
||||
Reference in New Issue
Block a user