Custom property author
Use this skill when adding or changing any cdx:* (or internal:*) property emitted into BOM output. The full property inventory and policy guidance is docs/CUSTOM_PROPERTIES.md.
Gate 0: prefer standard CycloneDX fields first
A custom property is a last resort. Before adding one, check whether the data fits a standard field:
supplier, manufacturer, authors, publisher — entity data
externalReferences — URLs
evidence.identity, evidence.occurrences — where a component was found
pedigree — lineage
hashes — digests (never a property)
licenses, scope — licensing and dependency scope
modelCard, formulation, component.data — AI/ML data
If a standard field works, use it. If a custom property is still necessary, it needs a clear namespace and a narrow purpose.
Gate 1: naming
- New properties use the
cdx:<ecosystem-or-context>:<field> convention (e.g. cdx:npm:hasInstallScript, cdx:github:workflow:triggers).
- Legacy unnamespaced properties were migrated to the
internal: prefix; do not add new unnamespaced properties, and treat internal:* as unstable implementation detail.
oci: and java:modules keep their existing namespaces for historical reasons.
Gate 2: value hygiene
Treat every value as potentially secret-bearing. CycloneDX serializes all property values as strings.
| Emit |
Do NOT emit |
Booleans: credentialExposure=true |
Raw tokens, passwords, API keys, cookies, session IDs, private keys |
Counts: credentialIndicatorCount=3 |
Raw environment variable values or command-line arguments |
Categories/field labels: header:Authorization |
The actual header/parameter values |
| Safe URL derivatives: scheme, host, basename |
URLs with query strings, fragments, userinfo, or signature params (token, sig, X-Amz-Signature, api_key, …) |
| Redacted markers or executable name only |
Full command lines, generated source contents, embedded file contents |
Value shapes already in use (keep consistent): booleans as "true"/"false", numbers as decimal strings, component-level lists comma-separated, BOM-level metadata lists newline-separated, timestamps as ISO 8601, structured payloads as JSON-serialized strings.
Gate 3: documentation (build-breaking)
lib/customProperties.poku.js scans every string literal matching cdx:... in non-test lib/**/*.js and fails the build if the property is absent from docs/CUSTOM_PROPERTIES.md. When adding a property:
- Add it to the appropriate property-family table (or inventory section) in
docs/CUSTOM_PROPERTIES.md, with the value shape and a policy-readiness label (hard deny / warning / context only).
- Add or extend a test asserting the new property is emitted as expected and that secrets are not copied into it.
- If the property creates an analyst pivot, check companion surfaces that stay aligned: BOM audit rules in
data/rules/*.yaml, docs/BOM_AUDIT.md, and bin/repl.js commands.
Review quick-check
- Unnamespaced or new
internal: property? Reject.
- Duplicates a standard CycloneDX field? Move to the standard field.
- Host-specific, non-reproducible, absolute local paths? Reject or redact.
- Structured data packed into CSV when a structured field exists? Reject.
- Secret-bearing value (even namespaced)? Replace with a count, boolean, host, or enum.
1---2name: custom-property-author3description: Guides defining and emitting new CycloneDX cdx: custom properties in cdxgen output, enforcing namespacing, safe value shapes (booleans, counts, categories instead of raw secrets, URLs, or commands), and the mandatory docs/CUSTOM_PROPERTIES.md documentation gate. Use when adding, changing, or reviewing cdx: or internal: properties on components, metadata, services, or evidence.4---56# Custom property author78Use this skill when adding or changing any `cdx:*` (or `internal:*`) property emitted into BOM output. The full property inventory and policy guidance is [docs/CUSTOM_PROPERTIES.md](../../docs/CUSTOM_PROPERTIES.md).910## Gate 0: prefer standard CycloneDX fields first1112A custom property is a last resort. Before adding one, check whether the data fits a standard field:1314- `supplier`, `manufacturer`, `authors`, `publisher` — entity data15- `externalReferences` — URLs16- `evidence.identity`, `evidence.occurrences` — where a component was found17- `pedigree` — lineage18- `hashes` — digests (never a property)19- `licenses`, `scope` — licensing and dependency scope20- `modelCard`, `formulation`, `component.data` — AI/ML data2122If a standard field works, use it. If a custom property is still necessary, it needs a clear namespace and a narrow purpose.2324## Gate 1: naming2526- New properties use the `cdx:<ecosystem-or-context>:<field>` convention (e.g. `cdx:npm:hasInstallScript`, `cdx:github:workflow:triggers`).27- Legacy unnamespaced properties were migrated to the `internal:` prefix; do not add new unnamespaced properties, and treat `internal:*` as unstable implementation detail.28- `oci:` and `java:modules` keep their existing namespaces for historical reasons.2930## Gate 2: value hygiene3132Treat every value as potentially secret-bearing. CycloneDX serializes all property values as strings.3334| Emit | Do NOT emit |35|---|---|36| Booleans: `credentialExposure=true` | Raw tokens, passwords, API keys, cookies, session IDs, private keys |37| Counts: `credentialIndicatorCount=3` | Raw environment variable values or command-line arguments |38| Categories/field labels: `header:Authorization` | The actual header/parameter values |39| Safe URL derivatives: scheme, host, basename | URLs with query strings, fragments, userinfo, or signature params (`token`, `sig`, `X-Amz-Signature`, `api_key`, …) |40| Redacted markers or executable name only | Full command lines, generated source contents, embedded file contents |4142Value shapes already in use (keep consistent): booleans as `"true"`/`"false"`, numbers as decimal strings, component-level lists comma-separated, BOM-level metadata lists newline-separated, timestamps as ISO 8601, structured payloads as JSON-serialized strings.4344## Gate 3: documentation (build-breaking)4546`lib/customProperties.poku.js` scans every string literal matching `cdx:...` in non-test `lib/**/*.js` and fails the build if the property is absent from `docs/CUSTOM_PROPERTIES.md`. When adding a property:47481. Add it to the appropriate property-family table (or inventory section) in `docs/CUSTOM_PROPERTIES.md`, with the value shape and a policy-readiness label (hard deny / warning / context only).492. Add or extend a test asserting the new property is emitted as expected **and** that secrets are not copied into it.503. If the property creates an analyst pivot, check companion surfaces that stay aligned: BOM audit rules in `data/rules/*.yaml`, `docs/BOM_AUDIT.md`, and `bin/repl.js` commands.5152## Review quick-check5354- Unnamespaced or new `internal:` property? Reject.55- Duplicates a standard CycloneDX field? Move to the standard field.56- Host-specific, non-reproducible, absolute local paths? Reject or redact.57- Structured data packed into CSV when a structured field exists? Reject.58- Secret-bearing value (even namespaced)? Replace with a count, boolean, host, or enum.