Module: Leakferret::Binary Private

Defined in:
lib/leakferret/binary.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Resolves (and, if needed, downloads) the native leakferret binary.

The binary is fetched into an absolute, user-writable cache directory rather than into the gem's own tree. RubyGems builds extensions in a throwaway temp dir, so anything written relative to the gem during gem install is discarded — the cache path sidesteps that entirely and also lets a plain gem install (no extension) work.

Constant Summary collapse

BUNDLED_DIR =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

A binary vendored inside the gem, if one was shipped (normally empty).

Pathname.new(__dir__).join('bin').freeze
CHECKSUMS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

SHA256 of each release tarball, pinned to BINARY_VERSION. The download is verified against these before the archive is ever unpacked, so a tampered or corrupted release asset is rejected instead of being executed. Because the digests live in the gem source, auditing the published gem tells you exactly which binary bytes it will run. Regenerate on every binary bump from the release's *.tar.gz.sha256 files.

{
  'aarch64-apple-darwin'     => '4db78de31252e8a2964528c817f12c73d46b660f453398c9c2f0feba6b370772',
  'aarch64-pc-windows-msvc'  => '021dce80072ae4cd553a23b05cddc9a878226668c92570df1b80c3221e034493',
  'x86_64-apple-darwin'      => 'b37747b336a3a18875b05a5c6e9aa736f53518fbcf8e722993f434cea41e0aa2',
  'x86_64-pc-windows-msvc'   => 'b0e32599f9d04e391d4b2d703e648c0a64739b15317434b0625eb00686cc6bb1',
  'x86_64-unknown-linux-gnu' => '05420ee2cd7e1d617f96caf098fda4a260ba361bc70af7e47da004959986cbbd'
}.freeze

Class Method Summary collapse

Class Method Details

.cache_dirPathname

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

User-writable cache directory, namespaced by the binary version so a gem upgrade fetches a fresh binary instead of reusing a stale one.

Returns:

  • (Pathname)

    the per-version cache directory



74
75
76
77
78
79
80
81
82
# File 'lib/leakferret/binary.rb', line 74

def cache_dir
  base =
    if Platform.windows?
      ENV['LOCALAPPDATA'] || File.join(Dir.home, 'AppData', 'Local')
    else
      ENV['XDG_CACHE_HOME'] || File.join(Dir.home, '.cache')
    end
  Pathname.new(base).join('leakferret', BINARY_VERSION)
end

.cache_pathPathname

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the cached binary's full path for this platform.

Returns:

  • (Pathname)

    the cached binary's full path for this platform



85
86
87
# File 'lib/leakferret/binary.rb', line 85

def cache_path
  cache_dir.join(Platform.binary_name)
end

.download_urlString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the GitHub release download URL for this platform's tarball.

Returns:

  • (String)

    the GitHub release download URL for this platform's tarball



90
91
92
93
# File 'lib/leakferret/binary.rb', line 90

def download_url
  'https://github.com/leakferrethq/leakferret/releases/download/' \
    "v#{BINARY_VERSION}/leakferret-#{BINARY_VERSION}-#{Platform.triple}.tar.gz"
end

.ensure!String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Download, checksum-verify, and unpack the binary into the cache. Idempotent: a no-op when the binary is already cached. The SHA256 is checked against the pinned CHECKSUMS value before anything is written or marked executable, so a tampered or truncated asset is rejected.

Returns:

  • (String)

    absolute path to the cached binary

Raises:

  • (BinaryNotFoundError)

    on an unknown platform, a checksum mismatch, or a binary missing from the downloaded archive



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/leakferret/binary.rb', line 103

def ensure!
  dest = cache_path
  return dest.to_s if dest.file?

  require 'fileutils'
  require 'open-uri'
  require 'zlib'
  require 'digest'
  require 'stringio'
  require 'rubygems/package'

  expected = CHECKSUMS[Platform.triple]
  if expected.nil?
    raise BinaryNotFoundError,
          "no pinned checksum for platform #{Platform.triple}; refusing to run an " \
          'unverified binary. Build from source and set LEAKFERRET_BIN instead.'
  end

  FileUtils.mkdir_p(dest.dirname)

  # Download the whole tarball, verify its SHA256 against the pinned value,
  # and only then unpack. Nothing is written to the cache (let alone marked
  # executable) until the bytes match, so a tampered or truncated release
  # asset is rejected rather than run.
  tarball = URI.open(download_url, &:read) # rubocop:disable Security/Open
  actual = Digest::SHA256.hexdigest(tarball)
  unless actual.casecmp?(expected)
    raise BinaryNotFoundError,
          "checksum mismatch for #{download_url}\n" \
          "  expected #{expected}\n  got      #{actual}\n" \
          'Refusing to install a binary that does not match the pinned hash.'
  end

  # Unpack in pure Ruby (no external `tar`, which on Windows mis-reads `C:\`
  # as a remote host). The archive nests everything under
  # leakferret-<version>-<triple>/, so match by basename.
  found = false
  Zlib::GzipReader.wrap(StringIO.new(tarball)) do |gz|
    Gem::Package::TarReader.new(gz) do |tar|
      tar.each do |entry|
        next unless entry.file?
        next unless File.basename(entry.full_name) == Platform.binary_name

        File.binwrite(dest, entry.read)
        found = true
      end
    end
  end
  raise BinaryNotFoundError, "binary not found inside #{download_url}" unless found

  FileUtils.chmod(0o755, dest) unless Platform.windows?
  dest.to_s
end

.install_instructions(candidate) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Human-readable fallback message shown when the binary is absent and the automatic download failed.

Parameters:

  • candidate (Pathname)

    the cache path the binary was expected at

Returns:

  • (String)

    multi-line manual-install instructions



162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/leakferret/binary.rb', line 162

def install_instructions(candidate)
  <<~MSG
    leakferret native binary not found, and the automatic download failed.

    Expected it at:
      #{candidate}

    Download the binary for your platform from:
      https://github.com/leakferrethq/leakferret/releases

    then either place it at the path above or point LEAKFERRET_BIN at it.
  MSG
end

.pathString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Absolute path to the native binary, downloading it on first use if necessary. Resolution order:

  1. LEAKFERRET_BIN — explicit override
  2. lib/leakferret/bin/ — a binary vendored in the gem
  3. the per-version cache — fetched on a prior run or at install
  4. download into the cache now

Returns:

  • (String)

    absolute path to the executable

Raises:

  • (BinaryNotFoundError)

    if the override is missing, or the binary is absent and cannot be downloaded



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/leakferret/binary.rb', line 49

def path
  override = ENV['LEAKFERRET_BIN']
  unless override.nil? || override.empty?
    unless File.file?(override)
      raise BinaryNotFoundError, "LEAKFERRET_BIN points to a missing file: #{override}"
    end

    return override
  end

  bundled = BUNDLED_DIR.join(Platform.binary_name)
  return bundled.to_s if bundled.file?

  return cache_path.to_s if cache_path.file?

  ensure!
  raise BinaryNotFoundError, install_instructions(cache_path) unless cache_path.file?

  cache_path.to_s
end