Summary

Using -o flag in sbom mode produces a runtime error, because it never specifies the output directory

Steps to Reproduce

  1. [[Blint]] sbom --src . -o sbom.json
  2. Generate runtime error
  09:49  main ?  …/blint/c_tests blint sbom --src . -o sbom.json
sbom.json
Traceback (most recent call last):
  File "/home/bay/Developer/blint/blint-py/bin/blint", line 8, in <module>
    sys.exit(main())
             ~~~~^^
  File "/home/bay/Developer/blint/blint-py/lib/python3.13/site-packages/blint/cli.py", line 255, in main
    run_sbom_mode(blint_options)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "/home/bay/Developer/blint/blint-py/lib/python3.13/site-packages/blint/lib/runners.py", line 45, in run_sbom_mode
    return generate(blint_options, exe_files, android_files)
  File "/home/bay/Developer/blint/blint-py/lib/python3.13/site-packages/blint/lib/sbom.py", line 173, in generate
    return create_sbom(
        components,
    ...<4 lines>...
        symbols_purl_map,
    )
  File "/home/bay/Developer/blint/blint-py/lib/python3.13/site-packages/blint/lib/sbom.py", line 235, in create_sbom
    os.makedirs(output_dir)
    ~~~~~~~~~~~^^^^^^^^^^^^
  File "<frozen os>", line 227, in makedirs
FileNotFoundError: [Errno 2] No such file or directory: ''

Root cause

Inside of sbom.py in the function create_sbom on line 235 we can see this code snippet inside here where sbom files are created.

  # Populate the dependencies
    sbom.dependencies = dependencies
    LOG.debug(
        f"SBOM includes {len(sbom.components)} components and {len(sbom.dependencies)} dependencies"
    )
    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

output_dir is created here, its value is being given from os.path.split(output_file)[0]. When we pass in the -o we can see that the produced value of output_dir is an empty string. The error also tells us this too FileNotFoundError: [Errno 2] No such file or directory: ''

Solution

Since by default when using Blint sbom, it’ll create a directory named reports. To keep confusion minimal I decided it be best to just name the output_dir for sbom mode reports.

But doing just this will place an output_dir reports and the the output_file sbom.json not inside the directory

  • To fix this instead of writing the file sbom.json we write it into the output_dir using an os.path.join
    else:
        output_dir = "reports"
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)
    
        file_write(
            os.path.join(output_dir, 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

Results after fix

After this fix, you’re able to use the -o flag to specify the name of the sbom that will be generated. It won’t break the -o flag for the non sbom mode because that itself specifies the output directory and we only modified the Create_sbom function.

▒▓  10:23  main !?  …/blint/c_tests blint sbom --src . -o sbom.json
 
░▒▓  10:23  main !?  …/blint/c_tests ls 2s 
reports  a.out  main.c
 
░▒▓  10:23  main !?  …/blint/c_tests ls reports/
sbom.json
 
░▒▓  10:23  main !?  …/blint/c_tests 
  • I also made sure to test it on a normal sbom output too as to not break things
░▒▓  10:24  main !?  …/blint/c_tests rm -rf reports/
 
░▒▓  10:24  main !?  …/blint/c_tests blint sbom --src .
 
░▒▓  10:24  main !?  …/blint/c_tests ls 2s 
reports  a.out  main.c
 
░▒▓  10:24  main !?  …/blint/c_tests ls reports/
bom-post-build.cdx.json
 
░▒▓  10:24  main !?  …/blint/c_tests