CodeBOM is a command-line tool that can scan for or verify software licensing declarations and visualize their implications. Its purpose is to help organizations leverage open source software. CodeBOM helps engineers to understand the implications of incorporating open source components into their products before requesting legal approval to distribute them. An engineering team armed with CodeBOM is one looking to maximize use of open source while minimizing time spent in the release pipeline.
To use CodeBOM, one creates a BOM file that describes the relationships between
a product's software components. The BOM author declares which directories
represent software components, the license each is distributed under, which
components are product dependencies, and which are used only for development.
One then uses the command-line tool codebom to verify that those declarations
are consistent with the codebase.
With a verified BOM in hand, downstream teams may use the declarations in different ways. A legal team may use them to ensure licensing terms are in compliance before distribution. A security team, on the other hand, may use code origin declarations to detect known security vulnerabilities.
CodeBOM includes a set of subcommands, scan, verify and graph. How you
use the subcommands will depend on what source code management tools you use.
If you use a package manager to download dependencies and build with a
traditional configure && make && make install, then your configure script
might use the data from the package manager to generate a BOM automatically. In
this case, your BOM would be ready to ship downstream immediately and
CodeBOM's scan and verify commands would only be useful as means to test
your configure script. If, on the other hand, your process is to copy
dependencies into your version control system (or are concerned others have),
then you can use codebom scan to help you detect missing declarations and
codebom verify to ensure all declarations are kept up to date.
Use Python's pip3 to install the latest release of CodeBOM from PyPI:
$ pip3 install codebomTo use the codebom graph command, install 'graphviz' and afterward, ensure dot
is in the system PATH.
$ sudo apt-get install graphviz$ brew install graphvizusage: codebom [-h] [--version] [-f FILE] {scan,verify,graph} ...
Validate a Bill of Materials
positional arguments:
{scan,verify,analyze,graph}
scan Scan the codebase for missing declarations
verify Verify declarations are consistent with the codebase
analyze Analyze potential implications of the declarations
graph Graph license dependencies
optional arguments:
-h, --help show this help message and exit
--version show program's version number and exit
-f FILE
Sample Bill of Materials .bom.yaml:
license: AllRightsReserved
license-file: LICENSE
dependencies:
- root: lib/gitpython
origin: https://github.com/gitpython-developers/GitPython/archive/0.3.6.tar.gz
license: BSD-3-Clause
license-file: LICENSE
development-dependencies:
- setup.py
- lib/pytestThe simplest .bom.yaml file is an empty file. It tells CodeBOM that
the directory containing .bom.yaml is the root directory and that
all code in that directory and its subdirectories contain proprietary
code. The following is equivalent to an empty .bom.yaml.
root: .When you run codebom scan, it traverses the directory structure in search
of evidence that the codebase is inconsistent with what has been declared
in the .bom.yaml. If codebom encounters a LICENSE file, for instance,
it will report an error and return a nonzero exit code.
$ touch .bom.yaml
$ touch LICENSE
$ codebom scan
.bom.yaml:1:1: error: Undeclared license file 'LICENSE' in directory '.'Declare the license file and try again:
$ echo 'license-file: LICENSE' >> .bom.yaml
$ codebom scanNo output. In Unix tradition, that translates to Great Success!
Now let's break it again.
$ mkdir -p foss
$ touch foss/LICENSE
$ codebom scan
.bom.yaml:1:1: error: Undeclared license file 'foss/LICENSE' in directory '.'Declare it and try again:
$ echo 'dependencies:' >> .bom.yaml
$ echo ' - root: foss' >> .bom.yaml
$ echo ' license-file: LICENSE' >> .bom.yaml
$ echo ' license: GPL-3.0' >> .bom.yaml
$ codebom scanNext, to verify the declarations are consistent with each other and the filesystem.
$ codebom verify
.bom.yaml:2:10: warning: The license 'Unknown' may be incompatible with the
license 'GPL-3.0' in 'foss'. If this dependency is used only for development,
move it to the 'development-dependencies' section.
To fix this, either declare a compatible license or move the dependency to the 'development-dependencies'.
development-dependencies:
- root: foss
license-file: LICENSE
license: GPL-3.0CodeBOM operates under the assumption we are verifying a binary distribution,
and therefore doesn't need to know the license of development dependencies used
to generate it - just that those directories can safely be ignored. CodeBOM will
only use the license information to verify source distributions, which can be
specified by adding the --source-distribution flag.
Wrapping up, a minimal BOM for the project above looks like this:
license-file: LICENSE
license: AllRightsReserved
development-dependencies:
- fossThe root directory contains two files, a .bom.yaml and a LICENSE file
stating this is proprietary software. The foss directory contains a
GPL LICENSE file, but is ignored by CodeBOM because the foss directory
is declared as a tool.
By default, CodeBOM looks for a file .bom.yaml in the current directory.
To specify a different location, use the -f flag. CodeBOM will interpret
the file from the directory containing that file.
Alternatively, to direct CodeBOM to a BOM at a different location, use a path to a BOM file anywhere you might declare the contents for a directory.
qosp/codebom/bom.yamlBOMs may reference other BOMs. In the dependencies or
development-dependencies section, CodeBOM will look for a .bom.yaml file in
the root directory.
dependencies:
- src/luaOr specify the BOM explicitly:
dependencies:
- src/lua/.bom.yamlIf src/lua contains the following BOM:
license: MITThen CodeBOM can generate a single merged BOM by specifying an output file:
$ codebom verify -o my-lua-app.yaml
$ cat my-lua-app.yaml
--- # Bill of Materials
license: Unknown
dependencies:
- root: src/lua
license: MITThe scan command reads a BOM and traverses its root directory and the root
directory of dependencies in the hopes of discovering missing declarations. It
will not traverse any dependency under development-dependencies unless the
--source-distribution flag is added. Likewise, it will not traverse component
BOMs unless the --recursive flag is added.
When the --add flag is present, the scan command can be used to generate
BOM files. It can generate a new BOM or start from an existing one. Rather than
reporting an error when it discovers a missing declaration, it adds the
declaration to a copy of the BOM. The resulting BOM is then written to the file
specified by the -o flag, which defaults to stdout. CodeBOM attempts to mimic
handwritten BOMs by coalescing missing declarations. To turn off automatic
coalescing, add --coalesce=none.
usage: codebom scan [-h] [--source-distribution] [--recursive] [--add]
[--coalesce {all,none}] [-o FILE]
optional arguments:
-h, --help show this help message and exit
--source-distribution
--recursive, -r Recurse into components
--add, -a Add missing declarations to output
--coalesce {all,none} Merge declarations
-o FILE
The verify command attempts to find contradictions in a BOM's declarations.
For example, if the user declares license: MIT, then codebom verify may
inspect the file at license-file to ensure its text is similar to the license
template defined by SPDX. By default, the
verify command will not access the network to do its verification. To check
that the contents of the origin URI matches the contents of the root
directory, use the --check-origins=contents flag. To check only that the
origin URI is valid network endpoint, use --check-origins=uri.
usage: codebom verify [-h] [--source-distribution] [-o FILE]
[--check-origins {uri,contents}]
optional arguments:
-h, --help show this help message and exit
--source-distribution
-o FILE
--check-origins {uri,contents}
The analyze command attempts to report the implications of the BOM's
declarations. For instance, if a project depends on another with a
more restrictive license and no licensees are declared, it will
output a message telling you there may be license conflict. The
analyze command is the textual counterpart to the graph command.
usage: codebom analyze [-h] [--source-distribution] [-o FILE]
optional arguments:
-h, --help show this help message and exit
--source-distribution
-o FILE
The graph command can be used to visualize the dependency graph and how
license terms may be inferred. It colors tainted nodes red, as well as
all edges from the top node down to the node that introduced the potential
licensing conflict.
usage: codebom graph [-h] [--source-distribution] [-o FILE]
optional arguments:
-h, --help show this help message and exit
--source-distribution
-o FILE
In the topsecret example, we can see how declaring
development-dependencies tells CodeBOM not to worry about a license conflict
in the context of a binary distribution.
$ codebom -f examples/topsecret/.bom.yaml graph -o topsecret-bindist.pngRunning the same command again but with the --source-distribution flag, we
can see how license terms can reach up to potentially derivative works.
$ codebom -f examples/topsecret/.bom.yaml graph --source-distribution -o topsecret-srcdist.pngA BOM is a YAML dictionary containing declarations that describe a software component.
bom : declaration* ;
declaration : origin
| root-matches-origin
| license
| license-file
| root
| files
| name
| copyright-holders
| licensees
| dependencies
| development-dependencies ;If you specify an origin URI, CodeBOM will download it and verify its contents
match the files in the root. If the URI points to a file, CodeBOM will
verify root points to a file as well and the file contents match. If the
files in root are from a subdirectory of origin, use a URI fragment to tell
CodeBOM the path to the files that match those in root.
--- # Bill of Materials
dependencies:
- root: lua/lua-5.3.0
origin: http://www.lua.org/ftp/lua-5.3.0.tar.gz#lua-5.3.0
license: MIT
license-file: doc/readme.html
- root: sha1
files: [sha1.c]
license-file: sha1.c
origin: http://downloads.sourceforge.net/project/jbig2dec/jbig2dec/0.11/jbig2dec-0.11.tar.gz#jbig2dec-0.11
license: PublicDomain
root-matches-origin: false- type:
string | null - default:
null
Setting the root-matches-origin property to false tells CodeBOM not to expect the file
contents to match. It exists so that you can always declare an origin for
your open source dependencies.
- type:
bool | null - default:
null
The license declaration may be an Identifier found at SPDX Licenses.
If the code is in the public domain, declare it 'PublicDomain'. If it is
proprietary code, declare it 'AllRightsReserved'. If the license is not listed
at spdx.org or you are not sure what the license is, declare it as Unknown.
- type:
string | null - default:
null
The path to the license file relative to the root declaration.
- type:
filepath | null - default:
null
The parent directory of all additonal declartions. It may be a path relative to the BOM file or an absolute path. If a dependency also specificies a root, it will be relative to the parent's root directory. For example:
--- myproject/bom.yaml
root: foo
license-file: LICENSE.txt
dependencies:
- root: bar
license-file: license.md$ codebom -f myproject/bom.yamlCodeBOM will expect to find LICENSE.txt at myproject/foo/LICENSE.txt and
license.md at myproject/foo/bar/license.md.
- type:
filepath - default:
.
List of files under root for which the remaining declarations apply to. A
null value implies all files under root including those in subdirectories.
- type:
[filepath] | null - default: null
Display name used by codebom graph. If null, CodeBOM will use the last
path element in the origin declaration. If the origin declaration is
null, CodeBOM will use the last path element in the root declaration.
- type:
string | null - default: null
A list of copyright holders. CodeBOM expects to find these names in the copyright notice.
- type:
[string] - default:
[]
A list of authorized clients. If a component has a restrictive license, the
verify command will warn if an unauthorized client adds it as a dependency.
To silence CodeBOM, ensure that at least one of the client's copyright-holders
is in the component's licenees.
- type:
[string] - default:
[]
A list of dependencies. Each item may be either a path to a BOM, a path to a
directory containing a .bom.yaml, a path to a root, or a BOM pertaining to
the the files at its root field.
- type:
[filepath | bom] - default:
[]
A list of dependencies used only for development.
- type:
[filepath | bom] - default:
[]


