Skip to content

fix: fail loudly when uuid cannot resolve device identity#31

Merged
MathiasKoch merged 1 commit into
masterfrom
fix/uuid-shim-fails-loudly
Jul 16, 2026
Merged

fix: fail loudly when uuid cannot resolve device identity#31
MathiasKoch merged 1 commit into
masterfrom
fix/uuid-shim-fails-loudly

Conversation

@MathiasKoch

Copy link
Copy Markdown
Member

Summary

getClientId() resolves the device UUID by running uuid on PATH. The indirection is deliberate — each platform supplies its own (Yocto via the fb-uuid recipe, the factory container via a shim reading /data/.device-uuid) and the plugin must not know any platform's on-disk layout. That part is fine.

What isn't: it swallowed every failure. The IOException was caught and printStackTrace()'d, a non-zero exit was never checked, stderr was discarded, and it returned null.

Null then reached sign():

NullPointerException: Cannot invoke "String.getBytes(String)" because "plainText" is null

NullPointerException is a RuntimeException, so the surrounding catch (GeneralSecurityException | IOException) missed it, and the nucleus logged only "Caught exception while running provisioning plugin. Moving on to run Greengrass without provisioning." Meanwhile the shim had already printed the actual answer — uuid: /data/.device-uuid missing — device not provisioned — to a stderr nobody read.

Three layers each discarded the diagnosis. Hit for real while bringing up a cloud-hosted Factbird Factory; it presents as a crypto bug and isn't one.

Changes

  • Check the exit code, capture stderr, throw DeviceProvisioningRuntimeException carrying the command's own message.
  • Move the getClientId() call out of the signing try-block — failing to resolve identity is not a signing error and shouldn't be reported as one.
  • Fix the logger: it was constructed for MqttConnectionHelper.class.
  • Add DeviceIdentityHelperTest — the class had no coverage. The exits_nonzero case reproduces the failure above and asserts the diagnosis survives.

A package-private getClientId(String[]) overload lets the tests point at a stub instead of depending on PATH. Public behaviour is unchanged.

`getClientId()` shells out to `uuid` on PATH — a deliberate abstraction, since
each platform supplies its own (Yocto via the fb-uuid recipe, the factory
container via a shim reading /data/.device-uuid). But it swallowed every
failure: the IOException was caught and printStackTrace()'d, a non-zero exit
was never checked, stderr was discarded, and it returned null.

Null then reached sign(), which failed with:

    NullPointerException: Cannot invoke "String.getBytes(String)"
    because "plainText" is null

That is a RuntimeException, so the surrounding catch (GeneralSecurityException
| IOException) missed it and the nucleus reported only "Caught exception while
running provisioning plugin" — while the shim's own diagnosis ("device not
provisioned") had already been thrown away with its stderr. Three layers each
discarded the answer.

Check the exit code, capture stderr, and throw
DeviceProvisioningRuntimeException with the command's own message. Move the
getClientId() call out of the signing try-block: failing to resolve identity is
not a signing error and must not be reported as one.

Also fix the logger, which was constructed for MqttConnectionHelper.class, and
add tests — the class had none.
@MathiasKoch
MathiasKoch merged commit 6431376 into master Jul 16, 2026
1 check passed
@MathiasKoch
MathiasKoch deleted the fix/uuid-shim-fails-loudly branch July 16, 2026 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant