Skip to content

setupCACerts: write the aggregate atomically and never reuse an empty or short one - #10

Open
Evan-Haug wants to merge 1 commit into
skiptools:mainfrom
Evan-Haug:fix/cacerts-atomic-write
Open

Evan-Haug wants to merge 1 commit into
skiptools:mainfrom
Evan-Haug:fix/cacerts-atomic-write

Conversation

@Evan-Haug

Copy link
Copy Markdown

setupCACerts: write the aggregate atomically and never reuse an empty or short one

AndroidBootstrap.setupCACerts creates cacerts-aggregate-<hash>.pem as an empty file under its final name, then writes the header and every certificate, and on a later launch reuses any existing file without checking it. If the process dies or a write fails between the create and the last write, the file is left empty or cut short under the final name and is trusted from then on. libcurl then has no trusted roots, so every https request fails and nothing is logged.

Observed on an Android 15 arm64 emulator with an app built on this package: a 0 byte cacerts-aggregate-*.pem (0 certificates) in the app cache, every https call failing at the certificate check; the same app with a fresh cache wrote 1,515,972 bytes (290 certificates) and connected. This reproduces on current main (d757c18) and tag 1.5.2.

Change

  • Build the aggregate in a temporary file in the same folder, synchronize() it, and rename(2) it into place, so the final name never holds a partial file. The temporary file is removed on every error path.
  • When an existing aggregate is found and force is false, reuse it only if it is non-empty and at least as large as the sum of the selected certificate files (a complete aggregate is a header plus the full bytes of those files, so it is never smaller). Otherwise remove it and regenerate.
  • No change to the certificate folders, the sort order, the checksum, the cache folder or the setenv.

Tests

Three Swift Testing tests, Android only: a complete bundle is built (four certificates, size at least the sum, no .tmp left); an empty bundle is rebuilt; bundles cut to 1 and 100 bytes are rebuilt, and a valid bundle is left untouched (same size and modification time).

Run with skip android test on an Android 15 arm64 emulator: the three new tests pass with this change, and with the same tests against the unmodified source the empty and truncated tests fail (the unmodified code reuses the bad file).

Limits worth knowing

The size comparison is a cheap guard for files left by older versions; a file cut inside the header sized slack at its end (about 190 bytes on a typical device) would still pass it. The atomic write is what prevents this library from producing a partial file in the first place.
testEmbedInCodeResource failed in my environment both with and without this change (probably CRLF line endings in a Windows checkout of Resources/sample_resource.txt), so it is unrelated.

🤖 Generated with Claude Code

@cla-bot

cla-bot Bot commented Oct 27, 2026

Copy link
Copy Markdown

Thank you for your pull request and welcome to the Skip community. We require contributors to sign our contributor license agreement (CLA), and we don't seem to have the user(s) @Evan-Haug on file. In order for us to review and merge your code, for each noted user please add your GitHub username to Skip's .clabot file

@Evan-Haug

Copy link
Copy Markdown
Author

@cla-bot recheck

… or short one

The aggregate cacerts file was created empty under its final name and then
filled, and an existing file was reused without any check. A process death or
write failure in between left an empty or truncated file that was trusted on
every later launch, so libcurl had no roots and every https request failed.

Write to a temporary file in the same folder, synchronize it and rename it into
place. Reuse an existing aggregate only when it is non-empty and at least as
large as the sum of the selected certificate files.

Add Android tests: complete bundle, empty bundle rebuilt, truncated bundle
rebuilt, valid bundle left untouched.
@Evan-Haug
Evan-Haug force-pushed the fix/cacerts-atomic-write branch from 809ded8 to f079391 Compare October 27, 2026 22:42
@cla-bot cla-bot Bot added the cla-signed label Oct 27, 2026
@Evan-Haug

Copy link
Copy Markdown
Author

@cla-bot recheck

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant