Using Templates
TL;DR
- Create custom formats:
syft <image> -o template -t ./template.tmpl - Templates receive same data as JSON output (explore with
syft <image> -o json) - Supports Sprig helper functions
Syft lets you define custom output formats using Go templates. This is useful for generating custom reports, integrating with specific tools, or extracting only the data you need.
How to use templates
Set the output format to template and specify the template file path:
syft <image> -o template -t ./path/to/custom.tmpl
You can also configure the template path in your configuration file:
#.syft.yaml
format:
template:
path: "/path/to/template.tmpl"
Available fields
Templates receive the same data structure as the syft-json output format. The Syft JSON schema is the source of truth for all available fields and their structure.
To see what data is available:
# View the full JSON structure
syft <image> -o json
# Explore specific fields
syft <image> -o json | jq '.artifacts[0]'
Key fields commonly used in templates:
.artifacts- Array of discovered packages.files- Array of discovered files.source- Information about what was scanned.distro- Detected Linux distribution (if applicable).descriptor- Syft version and configuration
Common package (artifact) fields:
.name,.version,.type- Basic package info.licenses- License information (array).purl- Package URL.cpes- Common Platform Enumerations.locations- Where the package was found
Template functions
Syft templates support:
- Go template built-ins - See the Go template documentation
- Sprig functions - Additional helpers from Sprig
- Syft-specific functions:
| Function | Arguments | Description |
|---|---|---|
getLastIndex | collection | Returns the last index of a slice (length - 1), useful for comma-separated lists |
hasField | obj, field | Checks if a field exists on an object, returns boolean |
Examples
The following examples show template source code and the rendered output when run against alpine:3.9.2:
CSV output
"Package","Version","Type","Found by"
{{- range .artifacts}}
"{{.name}}","{{.version}}","{{.type}}","{{.foundBy}}"
{{- end}}
"Package","Version","Type","Found by"
"alpine-baselayout","3.1.0-r3","apk","apk-db-cataloger"
"alpine-keys","2.1-r1","apk","apk-db-cataloger"
"apk-tools","2.10.3-r1","apk","apk-db-cataloger"
"busybox","1.29.3-r10","apk","apk-db-cataloger"
"ca-certificates-cacert","20190108-r0","apk","apk-db-cataloger"
"libc-utils","0.7.1-r0","apk","apk-db-cataloger"
"libcrypto1.1","1.1.1a-r1","apk","apk-db-cataloger"
"libssl1.1","1.1.1a-r1","apk","apk-db-cataloger"
"libtls-standalone","2.7.4-r6","apk","apk-db-cataloger"
"musl","1.1.20-r3","apk","apk-db-cataloger"
"musl-utils","1.1.20-r3","apk","apk-db-cataloger"
"scanelf","1.2.3-r0","apk","apk-db-cataloger"
"ssl_client","1.29.3-r10","apk","apk-db-cataloger"
"zlib","1.2.11-r1","apk","apk-db-cataloger"
Filter by package type
{{range .artifacts}}
{{- if eq .type "apk"}}
{{.name}}@{{.version}}{{end}}
{{- end}}
alpine-baselayout@3.1.0-r3
alpine-keys@2.1-r1
apk-tools@2.10.3-r1
busybox@1.29.3-r10
ca-certificates-cacert@20190108-r0
libc-utils@0.7.1-r0
libcrypto1.1@1.1.1a-r1
libssl1.1@1.1.1a-r1
libtls-standalone@2.7.4-r6
musl@1.1.20-r3
musl-utils@1.1.20-r3
scanelf@1.2.3-r0
ssl_client@1.29.3-r10
zlib@1.2.11-r1
Markdown report
# SBOM Report: {{.source.metadata.userInput}}
Scanned: {{.source.name}}:{{.source.version}} ({{.source.type}})
{{- if .distro}}
Distribution: {{.distro.prettyName}}
{{- end}}
## Packages ({{len .artifacts}})
| Package | Version | Type |
|---------|---------|------|
{{- range .artifacts}}
| {{.name}} | {{.version}} | {{.type}} |
{{- end}}
# SBOM Report: alpine:3.9.2
Scanned: alpine:3.9.2 (image)
Distribution: Alpine Linux v3.9
## Packages (14)
| Package | Version | Type |
| ---------------------- | ----------- | ---- |
| alpine-baselayout | 3.1.0-r3 | apk |
| alpine-keys | 2.1-r1 | apk |
| apk-tools | 2.10.3-r1 | apk |
| busybox | 1.29.3-r10 | apk |
| ca-certificates-cacert | 20190108-r0 | apk |
| libc-utils | 0.7.1-r0 | apk |
| libcrypto1.1 | 1.1.1a-r1 | apk |
| libssl1.1 | 1.1.1a-r1 | apk |
| libtls-standalone | 2.7.4-r6 | apk |
| musl | 1.1.20-r3 | apk |
| musl-utils | 1.1.20-r3 | apk |
| scanelf | 1.2.3-r0 | apk |
| ssl_client | 1.29.3-r10 | apk |
| zlib | 1.2.11-r1 | apk |
License compliance
{{range .artifacts}}
{{- if .licenses}}
{{.name}}: {{range .licenses}}{{.value}} {{end}}{{end}}
{{- end}}
alpine-baselayout: GPL-2.0
alpine-keys: MIT
apk-tools: GPL2
busybox: GPL-2.0
ca-certificates-cacert: GPL-2.0-or-later MPL-2.0
libc-utils: BSD
libcrypto1.1: OpenSSL
libssl1.1: OpenSSL
libtls-standalone: ISC
musl: MIT
musl-utils: BSD GPL2+ MIT
scanelf: GPL-2.0
ssl_client: GPL-2.0
zlib: zlib
Custom JSON subset
{
"scanned": "{{.source.metadata.userInput}}",
"packages": [
{{- $last := sub (len .artifacts) 1}}
{{- range $i, $pkg := .artifacts}}
{"name": "{{$pkg.name}}", "version": "{{$pkg.version}}"}{{if ne $i $last}},{{end}}
{{- end}}
]
}
{
"scanned": "alpine:3.9.2",
"packages": [
{ "name": "alpine-baselayout", "version": "3.1.0-r3" },
{ "name": "alpine-keys", "version": "2.1-r1" },
{ "name": "apk-tools", "version": "2.10.3-r1" },
{ "name": "busybox", "version": "1.29.3-r10" },
{ "name": "ca-certificates-cacert", "version": "20190108-r0" },
{ "name": "libc-utils", "version": "0.7.1-r0" },
{ "name": "libcrypto1.1", "version": "1.1.1a-r1" },
{ "name": "libssl1.1", "version": "1.1.1a-r1" },
{ "name": "libtls-standalone", "version": "2.7.4-r6" },
{ "name": "musl", "version": "1.1.20-r3" },
{ "name": "musl-utils", "version": "1.1.20-r3" },
{ "name": "scanelf", "version": "1.2.3-r0" },
{ "name": "ssl_client", "version": "1.29.3-r10" },
{ "name": "zlib", "version": "1.2.11-r1" }
]
}
Executable file digests
{{range .files -}}
{{- if .executable}}
{{.location.path}}: {{range .digests}}{{if eq .algorithm "sha256"}}{{.value}}{{end}}{{end}}
{{end}}
{{- end}}
/bin/busybox: 2c1276c3c02ccec8a0e1737d3144cdf03db883f479c86fbd9c7ea4fd9b35eac5
/lib/ld-musl-aarch64.so.1: 0132814479f1acc1e264ef59f73fd91563235897e8dc1bd52765f974cde382ca
/lib/libcrypto.so.1.1: 6c597c8ad195eeb7a9130ad832dfa4cbf140f42baf96304711b2dbd43ba8e617
/lib/libssl.so.1.1: fb72f4615fb4574bd6eeabfdb86be47012618b9076d75aeb1510941c585cae64
/lib/libz.so.1.2.11: 19e790eb36a09eba397b5af16852f3bea21a242026bbba3da7b16442b8ba305b
/sbin/apk: 22d7d85bd24923f1f274ce765d16602191097829e22ac632748302817ce515d8
/sbin/mkmntdirs: a14a5a28525220224367616ef46d4713ef7bd00d22baa761e058e8bdd4c0af1b
/usr/bin/getconf: 82bcde66ead19bc3b9ff850f66c2dbf5eaff36d481f1ec154100f73f6265d2ef
/usr/bin/getent: 53ffb508150e91838d795831e8ecc71f2bc3a7db036c6d7f9512c3973418bb5e
/usr/bin/iconv: 1c99d1f4edcb8da6db1da60958051c413de45a4c15cd3b7f7285ed87f9a250ff
/usr/bin/scanelf: 908da485ad2edea35242f8989c7beb9536414782abc94357c72b7d840bb1fda2
/usr/bin/ssl_client: 67ab7f3a1ba35630f439d1ca4f73c7d95f8b7aa0e6f6db6ea1743f136f074ab4
/usr/lib/engines-1.1/afalg.so: ea7c2f48bc741fd828d79a304dbf713e20e001c0187f3f534d959886af87f4af
/usr/lib/engines-1.1/capi.so: b461ed43f0f244007d872e84760a446023b69b178c970acf10ed2666198942c6
/usr/lib/engines-1.1/padlock.so: 0ccb04f040afb0216da1cea2c1db7a0b91d990ce061e232782aedbd498483649
/usr/lib/libtls-standalone.so.1.0.0: 7f4c2ff4010e30a69f588ab4f213fdf9ce61a524a0eecd3f5af31dc760e8006c
Find binaries importing a library
{{range .files -}}
{{- if .executable}}
{{- $path := .location.path}}
{{- range .executable.importedLibraries}}
{{- if eq . "libcrypto.so.1.1"}}
{{$path}}
{{break}}
{{- end}}
{{- end}}
{{- end}}
{{- end}}
/lib/libssl.so.1.1
/sbin/apk
/usr/lib/engines-1.1/afalg.so
/usr/lib/libtls-standalone.so.1.0.0
Troubleshooting
“can’t evaluate field” errors: The field doesn’t exist or is misspelled. Check field names with syft <image> -o json | jq.
Empty output: Verify your field paths are correct. Use syft <image> -o json to see the actual data structure.
Template syntax errors: Refer to the Go template documentation for syntax help.
Note
If you have templates from before Syft v0.102.0 that no longer work, set format.template.legacy: true in your configuration. This uses internal Go structs instead of the JSON output schema.
Long-term support for this legacy option is not guaranteed.
Next steps
Continue the guide
Next: Learn about Format Conversion to convert existing SBOMs between different formats without re-scanning.Additional resources:
- Template syntax: See Go template documentation for syntax reference
- Helper functions: Browse Sprig function documentation for available helpers
- Query with jq: Check Working with Syft JSON for query examples to use in templates
- Configuration: See Configuration options for persistent template settings