Rebrew is published as Beta (Development Status :: 4 - Beta in
pyproject.toml). There is no separate LTS track in this
repository: security fixes land on the default branch (main) and in the
next release cut from that branch. Older tags are not guaranteed to receive
backports.
Email the package author listed in pyproject.toml:
Marcel W. Wysocki: maci.stgn@gmail.com
Please include enough detail to reproduce the issue (affected command or module path, rebrew version or commit, and whether a local project or dependency is required). Do not open a public GitHub issue for unfixed vulnerabilities.
This repository does not define an SLA, bug bounty, or encrypted-reporting channel. Acknowledgement and fix timing depend on maintainer availability.
Rebrew is a local compiler-in-the-loop reversing workbench (CLI). It is not a multi-tenant network service. The living attack-surface and trust-boundary document is:
Operators should treat project source, configured HTTP endpoints
(recompile, LLM, ReVa MCP, decomp.me), GitHub release and toolchain-media
downloads (wibo, SDK tarballs), docker/podman images (REBREW_CONTAINER_RUNTIME),
cmake bridge wineprefix / profile pins (REBREW_WINEPREFIX, REBREW_TOOLCHAIN),
REBREW_SKILLS_DIR overlays, BinSync state-repo git remotes (rebrew binsync pull
fast-forwards and imports by default), a foreign splat project handed to
rebrew import-splat (its splat.yaml and symbol_addrs files are written into
the local project as metadata, source markers, and library_<module>.h names;
the command is a dry run unless --write is passed), and installed Python
entry-point plugins
(including rebrew.cache_backends) as part of the trust boundary. So are the
host-tool knobs outside the REBREW_ namespace: KUNA_SPECS (else the first
pypcode spec dir under UV_TOOL_DIR / XDG_DATA_HOME / LOCALAPPDATA) is the
SLEIGH language definition the native kuna binary parses, XDG_CACHE_HOME is
the sandbox base whose rebrew-prefixed directories older than a day are
deleted by sweep_stale_temp_dirs on the next compile
(src/rebrew/temp_dirs.py). That sweep is not confined to the cache base: it
also deletes, by the same age rule, directories under the project at
.rebrew/climb and .rebrew/qualsweep, where the prefix it matches is
declared by the caller and derived from the function's symbol name
(src/rebrew/climb.py :633, src/rebrew/qual_sweep.py :244), so a
symbol name in shipped metadata decides what a later run may remove from
those two rebrew-owned scratch directories, and nothing else. And
XAUTHORITY names the X cookie file rebrew
adopts for host-wine helpers and re-exports to wine children, accepted as any
file the analyst can read (_local_cookie in src/rebrew/headless.py), so the
headless-display access decision is taken from the process environment. On a multi-user host the headless X display is a boundary, and on both
of its paths the access control is not in force. The Xvfb rebrew starts is
handed an -auth file whose contents are a bare hex token, not an xauth
record (address display protocol name data), so no cookie can be read out
of it (_new_cookie in src/rebrew/headless.py); the same applies to
adopting an Xvfb already on the box, where _adopt falls back to the
operator's XAUTHORITY when the candidate server advertises no -auth
cookie of its own. Nothing in the module opens an X connection to verify.
Treat the display wine draws on as unauthenticated, and note that
XAUTHORITY is written into os.environ, so every later child process
inherits it. The display's pid is resolved through /proc/*/cmdline on
the process name Xvfb, which is world-readable.
CI is a boundary of its own: pull_request (never pull_request_target),
workflow permissions: contents: read, no id-token, persist-credentials: false,
and every third-party Action pinned by commit SHA. secrets.GITHUB_TOKEN is
never workflow-level env: in ci.yml it reaches only the resembl clone
step through the composite action's github-token input, and in
.github/workflows/toolchain-sync.yml it reaches the resembl clone step and
the nightly check-updates run. That workflow runs nightly and reports
toolchain pin drift without applying it. There is no publish step in this
repository: the PyPI upload is a manual uv publish from a dist/ the package
job verified, so a release is not attributable to a workflow run. That command
does upload a PEP 740 attestation beside the files, which names the publisher
who uploaded them, not this repository; nothing in CI signs artifacts.
The packaged compile-cache backends refuse pickle deserialize
(NoPickleDisk in src/rebrew/compile_cache.py); that does not attest
plugin cache backends or remove the open upstream diskcache advisory.
- No claim of authentication or authorization on
rebrew dashboard(default bind is loopback; binding to non-loopback addresses exposes a read-only HTTP API without credentials). The only gate on those routes is the Host allow-list; there is no rate limit, and the 64-connection cap (_MAX_ACTIVE_CONNECTIONSinsrc/rebrew/dashboard.py) is an availability bound an allow-listed client can fill.GET /api/healthreports the configured target count to any client that clears it, and the absolute coverage-directory path only when the bind is127.0.0.1/localhost/::1; a non-loopback--hostdrops that field (expose_pathsinsrc/rebrew/dashboard.py). The flag is decided by the--hoststring, not by the address the socket bound, so alocalhostthat resolves off-box would still serve the path. - No claim that docker toolchain or cmake-bridge execution is a hardened
sandbox against a hostile project tree or malicious image. Local container
runs use
--network=none(no egress) andno-new-privileges; that does not imply escape resistance. The compile path passes no--user, so the compiler runs as the image's default user; it mounts the project root read-only but can read all of it. The cmake bridge mounts the project root and wineprefix read-write. - No claim that "docker-only" covers toolchains registered by an entry point
or a
REBREW_TOOLCHAIN_OVERLAY_DIRTOML file: a spec withoutimageruns itsbinaryon the host with the full process environment (run_toolchaininsrc/rebrew/toolchain.py). - No claim that optional wibo / toolchain-media downloads are attested beyond
the in-code host allow-list and hash checks described in
docs/THREAT_MODEL.md. Wibo asset bytes are allow-listed, redirect-checked per hop, and checked against the releasedigest; the release-metadata GET that supplies that digest is pinned toapi.github.comper hop (wibo.py_trusted_wibo_metadata_url/_read_release_metadata, which usesfollow_redirects=False). None of that is content attestation: the digest is whatever the current GitHub release publishes. Wibo does not consumeGH_TOKEN/GITHUB_TOKEN(those tokens are used only by toolchain pin-check/update HTTP to GitHub). - No claim that dependency CVEs are absent; pin rationale lives in
pyproject.tomlcomments and the changelog. In particular, diskcache's open pickle advisory is mitigated for packaged backends byNoPickleDisk, not by claiming the upstream advisory is fixed. - No claim that library helpers which invoke host DOSBox
(
rebrew.msvc16/tc16/delphi16viarebrew.dosbox) are covered by the docker-only compile guarantee on the shipped CLI compile path. There is nopdb_cvdumpmodule and noREBREW_CVDUMPsetting. PDB reads on the shipped path run hostllvm-pdbutil(rebrew.pdb_info/rebrew.toolchain_detect), not winecvdump.exe. - No claim that every shipped CLI command stays inside a container.
rebrew calibrate-bssexecutes the link command read from the project'sbuild/CMakeFiles/*/link.txtand its--compile-cmdon the host,rebrew link-sweepexecutes that samelink.txtcommand on the host,rebrew gen-stubs --build-cmdexecutes an operator-supplied build command on the host,rebrew build-check --objectsruns hostmake -q -f <build.make>in the build directory, and analysis helpers run host rizin/r2, kuna, objconv, llvm-pdbutil, diec, objdump, and nasm against target binaries. Linked-exe GA (match_ga.py_compile_source/matcher/compiler.pybuild_candidate) runs that host compiler only when the profile has no docker image; a profile whose spec has an image raises instead of starting host wine.rebrew doctorsmoke-runs host wine or wibo plusCL.EXEonly on that same image-less path (check_compiler); a docker-backed profile returns before the smoke. Treat a project tree from an untrusted source as able to run code on the host through the paths that do execute.make -qsuppresses makefile recipes, not$(shell …)/$(info …)expansion while the file is read, so thebuild.makeCMake generates for a cloned project is host-executable too. - No claim that reading a project's build files stays inside the project
directory. The cmake bridge resolves and reads every
@rspresponse file in its argv on the host, with no containment check on the resolved path, and stages the rewritten copy in a temporary directory inside the read-write project root (src/rebrew/cmake_tc.py_rewrite_response_files, called from_docker_run). - No claim that a cloned project's metadata can make rebrew read or
write outside the project, and no claim that a command other than the
validator enforces it. The
fileidentity field is a path, and one validator owns it on the compile/verify/rename/cross-import path:contained_path(src/rebrew/sources.py) resolves the value undersource_roots(reversed_dir, thenshared_dir, with the projectrootas the outer bound, since a shared-tree source is recorded../-prefixed relative toreversed_dir) and refuses an empty, absolute, or escaping value.rebrew verify(verify.pyverify_entry, where a refused entry is recordedMISSING_FILE), that module's cache-validity read and deferred STATUS pass, the batch compile (compile.pyprecompile_batch), the blocker clear inrebrew test, therebrew-objdiff-buildshim (objdiff_project.py),rebrew merge-sweep, and every read and write inrebrew cross-import(cross_import.pyimport_function,import_shared_function,promote_to_shared) all resolve through it;rebrew rename(rename.py,rename_ops.py) and the verify cache's patch refresh (verify_cache.py) keep their own resolve-and-compare. BinSync overlay also usescontained_pathfor source reads and signature/comment targets; prototype imports update the actual C signature through the AST and the importer's source-tree check. What this does not claim: the shared validator's outer bound is the projectroot, so project-internal paths such as.git/hooks/,rebrew-project.toml, or.rebrew/can pass that validator. It says nothing about a project running host commands, whichdocs/THREAT_MODEL.md§4 covers separately. - No claim that
REBREW_CONTAINER_RUNTIMEis restricted to a container runtime rebrew trusts.container_runtime(src/rebrew/utils.py) rejects characters outside^[a-zA-Z0-9_\-\./]+$and, for a value with no path separator, any name outsidedocker/podman/nerdctl. That is a typo guard: a value containing/is spawned as a path to the binary, so the knob remains a full redirection of every container run. - No claim that the project tree cannot redirect outbound traffic. A
project's
[compiler] recompile_urlapplies unlessREBREW_RECOMPILE_URLis set, and its[llm] endpointoverridesREBREW_LLM_ENDPOINT, so a cloned project chooses where the function source is sent (recompile_urlinsrc/rebrew/compile.py,llm_configinsrc/rebrew/llm_seed.py). The one thing the project cannot take is the operator's environment key:llm_configrefuses to sendREBREW_LLM_API_KEYto a non-loopback[llm].endpointunlessREBREW_LLM_ALLOW_PROJECT_ENDPOINT=1is set (_project_endpoint_allowedinsrc/rebrew/llm_seed.py). That gate reads the key's provenance, so a key committed in the project TOML still travels to that project's own endpoint, and[compiler] recompile_urlis not gated at all. - No claim that a recompile endpoint only receives the source. The object it
returns is written to the compile workdir and published into the local
compile cache (
publish_obj_cacheinsrc/rebrew/compile.py), keyed on source, flags, andrecompile:<url>/<spec.name>, and later runs are served those bytes instead of compiling. A hostile endpoint, or a MITM on the plainhttpleg the URL check still allows, therefore plants bytes that persist across runs, carry no provenance marker, are never expired, and are handed to LIEF's COFF parser (or hostobjconvon an OMF profile) on every replay. The response body has no size cap; the cachesize_limitis the only bound, and that too is project-supplied: the 500 MiB default (_DEFAULT_SIZE_LIMITinsrc/rebrew/compile_cache.py) is replaced by[cache] size_limit_mibfromrebrew-project.tomlwith no upper clamp (cache_size_limitinsrc/rebrew/config.py). Changing the endpoint or toolchain starts from a cache miss, andrebrew cache cleardrops the entries a same-endpoint change leaves in place. - No claim that a decomp.me upload is revocable, or that it leaves the host
over TLS.
POST /api/scratchhas no idempotency key and mints a public scratch, whose slug andclaim_tokenexist only in the reply. A read timeout or dropped connection can therefore hide a create the service already committed, leaving the uploaded function source, ASM, and context in a public scratch that neither the analyst norrebrewcan claim or delete.rebrew decompmeno longer re-POSTs after a post-send transport failure (_never_deliveredinsrc/rebrew/decompme.py), so a single run does not mint a second orphan, but a hand-run retry still does, and the existing orphan is not discoverable from the CLI.validate_http_urladmits plainhttpto any host, so a non-TLS--apisends the function source, its object, and the returnedclaim_tokenin cleartext. - No claim that a decomp.me or other outbound URL is pinned in transport.
No HTTP call in the package passes
trust_env, soHTTPS_PROXY,SSL_CERT_FILE, andREQUESTS_CA_BUNDLEapply to the wibo asset and metadata fetches, the toolchain media download, and theGH_TOKEN-bearing GitHub commit lookup, none of which is size-capped. - No claim that
rebrew import-splatconstrains the files a foreign config names to the foreign project.splat_config._rel(splat_config.py:516-521) resolves everysplat.yamlpath against the operator's base with no containment check, so an absolute,../.., or backslash-separated entry makes the command read a host file andshutil.copy2it into the project.--writeis still the only opt-in.