Bug 1 (fixed): JSONDecodeError: Extra data when parsing JSON output
Description
When testssl runs on multiple targets, the --jsonfile output contains multiple concatenated JSON structures. The on_cmd_done() method uses json.load() which expects a single JSON object, causing a JSONDecodeError.
Root Cause
Secator's runner can load the same target multiple times (Loaded 2 targets for testssl with the same host). Each target's testssl run appends to the same output file. The resulting file contains concatenated JSON:
[
{"id": "engine_problem", ...},
{"id": "scanProblem", ...}
]
{
"id": "scanProblem",
"finding": "non-empty 'testssl.json' exists. Either use '--append' or (re)move it"
}
json.load() fails with Extra data: line 24 column 10 (char 909).
Secondary error
After the JSONDecodeError, the cleanup code calls os.killpg() on an already-terminated process, causing ProcessLookupError: [Errno 3] No such process. This is a side effect. The process is already dead.
Traceback
json.decoder.JSONDecodeError: Extra data: line 24 column 10 (char 909)
Traceback (most recent call last):
File ".../secator/tasks/testssl.py", line 92, in on_cmd_done
data = json.load(f)
...
json.decoder.JSONDecodeError: Extra data
During handling:
File ".../secator/runners/command.py", line 645, in stop_process
os.killpg(os.getpgid(self.process.pid), sig)
ProcessLookupError: [Errno 3] No such process
Fix
Replace json.load(f) with incremental parsing using json.JSONDecoder().raw_decode():
# Before (crashes on concatenated JSON)
with open(self.output_path, 'r') as f:
data = json.load(f)
# After (handles single array, concatenated arrays/objects, JSONL)
with open(self.output_path, 'r') as f:
content = f.read()
decoder = json.JSONDecoder()
data = []
pos = 0
while pos < len(content.strip()):
while pos < len(content) and content[pos] in ' \t\n\r,':
pos += 1
if pos >= len(content):
break
try:
obj, end = decoder.raw_decode(content, pos)
if isinstance(obj, list):
data.extend(obj)
else:
data.append(obj)
pos = end
except json.JSONDecodeError:
break
This handles all observed formats: single JSON array (normal case), concatenated arrays, array + object, JSONL.
Bug 2 (needs maintainer input): Duplicate targets write to same output file
Description
The output path in on_cmd() uses self.unique_name which is the same for all instances. When multiple instances of testssl run, they all write to the same file. If the file already exists from a previous run, testssl.sh refuses to overwrite it:
[ERR] non-empty '/home/secator/.secator/reports/.outputs/testssl.json' exists. Either use '--append' or (re)move it
Root Cause
self.unique_name resolves to just testssl regardless of the target, so all instances share the same output path.
Possible approaches (for maintainers to decide)
- Delete the file before each run in
on_cmd()
- Use a truly unique path (e.g., with target + UUID)
Bug 3 (needs maintainer input): Missing libproviders.so in Docker image
Description
testssl.sh reports being unable to resolve any target:
[ERR] No IPv4/IPv6 address(es) for 'example.com' available
However DNS resolution works fine in the container (nslookup resolves correctly). After investigating, i found that the bundled OpenSSL is missing libproviders.so, which causes testssl.sh to fail before it can scan:
Error configuring OpenSSL
error:25066067:DSO support routines:DLFCN_LOAD:could not load the shared library:
filename(libproviders.so): libproviders.so: cannot open shared object file: No such file or directory
find confirms no .so files exist in the testssl installation directory (/home/secator/.local/share/testssl.sh_v3.2.0/).
Possible approaches (for maintainers to decide)
- Include OpenSSL provider libraries in the Docker image
- Configure testssl to use the system OpenSSL instead of the bundled one
- Update the testssl.sh installation method
Environment
- secator v0.30.1 (Docker image
freelabz/secator:0.30.1)
- Python 3.12
- testssl.sh v3.2.0
- Tested via Celery worker in Docker
Bug 1 (fixed):
JSONDecodeError: Extra datawhen parsing JSON outputDescription
When testssl runs on multiple targets, the
--jsonfileoutput contains multiple concatenated JSON structures. Theon_cmd_done()method usesjson.load()which expects a single JSON object, causing aJSONDecodeError.Root Cause
Secator's runner can load the same target multiple times (
Loaded 2 targets for testsslwith the same host). Each target's testssl run appends to the same output file. The resulting file contains concatenated JSON:[ {"id": "engine_problem", ...}, {"id": "scanProblem", ...} ] { "id": "scanProblem", "finding": "non-empty 'testssl.json' exists. Either use '--append' or (re)move it" }json.load()fails withExtra data: line 24 column 10 (char 909).Secondary error
After the
JSONDecodeError, the cleanup code callsos.killpg()on an already-terminated process, causingProcessLookupError: [Errno 3] No such process. This is a side effect. The process is already dead.Traceback
Fix
Replace
json.load(f)with incremental parsing usingjson.JSONDecoder().raw_decode():This handles all observed formats: single JSON array (normal case), concatenated arrays, array + object, JSONL.
Bug 2 (needs maintainer input): Duplicate targets write to same output file
Description
The output path in
on_cmd()usesself.unique_namewhich is the same for all instances. When multiple instances of testssl run, they all write to the same file. If the file already exists from a previous run, testssl.sh refuses to overwrite it:Root Cause
self.unique_nameresolves to justtestsslregardless of the target, so all instances share the same output path.Possible approaches (for maintainers to decide)
on_cmd()Bug 3 (needs maintainer input): Missing
libproviders.soin Docker imageDescription
testssl.sh reports being unable to resolve any target:
However DNS resolution works fine in the container (
nslookupresolves correctly). After investigating, i found that the bundled OpenSSL is missinglibproviders.so, which causes testssl.sh to fail before it can scan:findconfirms no.sofiles exist in the testssl installation directory (/home/secator/.local/share/testssl.sh_v3.2.0/).Possible approaches (for maintainers to decide)
Environment
freelabz/secator:0.30.1)