Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -1415,19 +1415,30 @@ def quick_validate(flow_path: str) -> bool | None:
has_start = False
has_banned = False
try:
with open(flow_path, 'r') as fp:
flow_data = fp.read()
for start_tag in START_ELEMS_TAGGED:
if start_tag in flow_data:
has_start = True
break

for banned_tag in BANNED_ELEMS_TAGGED:
if banned_tag in flow_data:
has_banned = True
break

return has_start and not has_banned
# Flow XML is authored/declared UTF-8, so read it as UTF-8 explicitly
# rather than relying on the platform locale encoding (e.g. cp1252 on
# Windows), which would raise UnicodeDecodeError on valid flows and
# cause them to be silently dropped. Fall back to cp1252 for legacy
# files, matching the pattern used in flow_scanner/__main__.py.
try:
with open(flow_path, 'r', encoding='utf-8') as fp:
flow_data = fp.read()
except UnicodeDecodeError:
# cp1252 is used on older Windows systems
with open(flow_path, 'r', encoding='cp1252') as fp:
flow_data = fp.read()

for start_tag in START_ELEMS_TAGGED:
if start_tag in flow_data:
has_start = True
break

for banned_tag in BANNED_ELEMS_TAGGED:
if banned_tag in flow_data:
has_banned = True
break

return has_start and not has_banned

except FileNotFoundError:
logger.critical(f"Could not find file {flow_path}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Tests for public.parse_utils.

Run from the FlowScanner directory with::

python3 -m unittest tests.test_parse_utils
"""
import builtins
import os
import tempfile
import unittest

import public.parse_utils as pu


class QuickValidateEncodingTest(unittest.TestCase):
"""Regression tests for issue #2074.

Flow XML is authored/declared UTF-8, but quick_validate historically opened
files using the platform locale encoding. On Windows (cp1252) any flow
containing a non-cp1252 UTF-8 byte (e.g. 0x9D from U+201D smart quote)
raised UnicodeDecodeError, the flow was silently dropped, and the scanner
ultimately failed to write its results file.
"""

#: A valid flow whose description contains a right double quotation mark
#: (U+201D). This encodes to byte 0x9D in UTF-8's continuation, which maps
#: to <undefined> in cp1252 and would raise UnicodeDecodeError.
UTF8_FLOW = (
'<?xml version="1.0" encoding="UTF-8"?>\n'
'<Flow xmlns="http://soap.sforce.com/2006/04/metadata">'
'<description>smart ”quote</description>'
'<start></start>'
'</Flow>'
)

def setUp(self):
fd, self.flow_path = tempfile.mkstemp(suffix='.flow')
os.close(fd)
with open(self.flow_path, 'w', encoding='utf-8') as fp:
fp.write(self.UTF8_FLOW)

def tearDown(self):
if os.path.exists(self.flow_path):
os.unlink(self.flow_path)

def test_utf8_flow_validates_under_cp1252_locale(self):
"""quick_validate must read UTF-8 flows even when the locale is cp1252."""
orig_open = builtins.open

def cp1252_default_open(file, mode='r', *args, **kwargs):
# Emulate a Windows cp1252 locale: text reads with no explicit
# encoding fall back to cp1252.
if 'b' not in mode and 'encoding' not in kwargs:
kwargs['encoding'] = 'cp1252'
return orig_open(file, mode, *args, **kwargs)

builtins.open = cp1252_default_open
try:
result = pu.quick_validate(self.flow_path)
finally:
builtins.open = orig_open

self.assertTrue(result)

def test_utf8_flow_validates_under_utf8_locale(self):
"""quick_validate still works under a normal UTF-8 locale."""
self.assertTrue(pu.quick_validate(self.flow_path))

def test_missing_file_returns_none(self):
"""A missing file still returns None (unchanged behavior)."""
self.assertIsNone(pu.quick_validate(self.flow_path + '.does-not-exist'))


if __name__ == '__main__':
unittest.main()
2 changes: 1 addition & 1 deletion packages/code-analyzer-flow-engine/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@salesforce/code-analyzer-flow-engine",
"description": "Plugin package that adds 'Flow Scanner' as an engine into Salesforce Code Analyzer",
"version": "0.40.0",
"version": "0.40.1-SNAPSHOT",
"author": "The Salesforce Code Analyzer Team",
"license": "BSD-3-Clause",
"homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview",
Expand Down
Loading