Summary

when running [[Blint]] sbom --src . --stdout on Linux specifically it’ll crash giving a specific runtime report about a TypeError: expected str, bytes or os.PathLike object, not TextIOWrapper.

This error indicates that you’re trying to use a string method or an operation (like —stdout) that expects a file path on a file object.

Steps to reproduce

  1. Run [[Blint]] sbom --src . --stdout on any binary (C, Rust, Go, Windows etc (i did test it on windows exe since i use virtualboxes))
  2. Observe the generated error
Traceback (most recent call last):
  File "/home/bay/Developer/test_c/blint-py/bin/blint", line 6, in <module>
    sys.exit(main())
             ~~~~^^
  File "/home/bay/Developer/test_c/blint/blint/cli.py", line 255, in main
    run_sbom_mode(blint_options)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "/home/bay/Developer/test_c/blint/blint/lib/runners.py", line 45, in run_sbom_mode
    return generate(blint_options, exe_files, android_files)
  File "/home/bay/Developer/test_c/blint/blint/lib/sbom.py", line 172, in generate
    return create_sbom(
        components,
    ...<4 lines>...
        symbols_purl_map,
    )
  File "/home/bay/Developer/test_c/blint/blint/lib/sbom.py", line 205, in create_sbom
    output_dir = os.path.split(output_file)[0]
                 ~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "<frozen posixpath>", line 103, in split
TypeError: expected str, bytes or os.PathLike object, not TextIOWrapper

Root cause

in sbom.py, the following function create_sbom

# line 203 of sbom.py
def create_sbom(
    components: list[Component],
    dependencies: list[dict],
    output_file: str, 
    sbom: CycloneDX,
    deep_mode: bool,
    symbols_purl_map: dict,
) -> CycloneDX:
    output_dir = os.path.split(output_file)[0]
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    # Populate the components
    sbom.components = trim_components(components)
    # If we have only one parent component then promote it to metadata.component
    if sbom.metadata.component.components:
        if len(sbom.metadata.component.components) == 1:
            sbom.metadata.component = sbom.metadata.component.components[0]
        else:
            root_depends_on = [
                ac.bom_ref.model_dump(mode="python")
                for ac in sbom.metadata.component.components
            ]
            dependencies.append(
                {
                    "ref": sbom.metadata.component.bom_ref.model_dump(mode="python"),
                    "dependsOn": root_depends_on,
                }
            )
    # Populate the dependencies
    sbom.dependencies = dependencies
    LOG.debug(
        f"SBOM includes {len(sbom.components)} components and {len(sbom.dependencies)} dependencies"
    )
    file_write(
        output_file,
        sbom.model_dump_json(
            indent=None if deep_mode else 2,
            exclude_none=True,
            exclude_defaults=True,
            warnings=False,
            by_alias=True,
        ),
        log=LOG,
    )
    return sbom
 
 
  • The issue lies within the function handling and assuming the output_file is a string.
  • when running with —stdout flag enabled, if you print the current contents it shows this
<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>

Solution

I’ve made a fork of the project that can be accessed here: betim - blint

The update I’ve made changes the function a bit and adds a new import sys into sbom.py.

Instead of assuming the filename to be a string, we first just populate our sbom content variable., then populate the dependencies.

After that we check if the output_file is set to stdout, if it is, we just print to the terminal with print(). else we go on normally and create our custom named sbom.json object

if output_file is sys.stdout:
        print(sbom.model_dump_json(indent=2, exclude_none=True, exclude_defaults=True, warnings=False, by_alias=True))
    else:
        output_dir = os.path.split(output_file)[0]
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)

    
        file_write(
            output_file,
            sbom.model_dump_json(
                indent=None if deep_mode else 2,
                exclude_none=True,
                exclude_defaults=True,
                warnings=False,
                by_alias=True,
            ),
            log=LOG,
        )
    return sbom

as I’m inspecting this, the -o and --output-file isn’t working either (will be a seperate issue)

Result after Fix

with updating the program, you can see it prints out the sbom in stdout, this will be useful with other unix-like command line utilities.

  • (note in final PR the io.TextIOWrapper will not appear (it’s a print statement) I’ve also tested it with various binaries to ensure nothing was broken in the process of this:
  • PE (dll, x86, x64, dotnet)
  • Go
  • Rust