Skip to content

fix(tasks): testssl JSONDecodeError on concatenated JSON output, duplicate targets collision, missing OpenSSL libproviders.so in Docker image #1056

Description

@sundayz-hunter

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions