Skip to content

Instantly share code, notes, and snippets.

@andrew
Created April 21, 2026 09:52
Show Gist options
  • Select an option

  • Save andrew/149cf715010d526965972f890a870ff4 to your computer and use it in GitHub Desktop.

Select an option

Save andrew/149cf715010d526965972f890a870ff4 to your computer and use it in GitHub Desktop.

pacslock

Statically detectable package capabilities

Like capslock but for detecting capabilites in package metadata and archive structure rather than callgraph analysis.

Package capability detection ideas

Ruby (RubyGems)

  • C extensions declared (extensions field in gemspec)
  • Extension build files present (extconf.rb, mkrf_conf.rb, Rakefile listed as extension)
  • Rust extension indicators (Cargo.toml in archive, rb_sys in runtime/dev deps)
  • Precompiled native gem (platform field ≠ ruby: -x86_64-linux, -arm64-darwin, etc.)
  • Executables declared (executables field in gemspec, bin/ or exe/ directory)
  • Post-install message (post_install_message field set)
  • FFI usage intent (ffi gem in dependencies)
  • Shared libraries in archive (.so, .bundle file presence)
  • Required Ruby version constraints (informational)

PyPI

  • setup.py present in sdist (arbitrary code at build)
  • setup.cfg present
  • pyproject.toml build backend (standard: setuptools/hatchling/flit/poetry/pdm; non-standard flagged)
  • Rust build backend (maturin, setuptools-rust as backend or in build-requires)
  • C/Fortran extension intent (build-requires including cython, pybind11, numpy)
  • Wheel with native binaries (.so, .pyd, .dylib file presence in wheel)
  • Platform-specific wheel (wheel tag ≠ py3-none-any: manylinux, musllinux, macosx, win)
  • Console script entry points ([project.scripts] or console_scripts)
  • GUI script entry points ([project.gui-scripts] or gui_scripts)
  • Arbitrary entry point groups (plugin injection into other packages)
  • sdist vs wheel-only availability (sdist = consumer will execute build code)
  • FFI intent (cffi, ctypes-using deps declared)
  • no-build-isolation required (in pyproject.toml or setuptools config)

JavaScript (npm)

  • Lifecycle scripts declared (preinstall, install, postinstall, prepare, prepublish, prepublishOnly)
  • bin entries (shell-accessible executables)
  • Native build config (binding.gyp present in archive)
  • node-gyp as dev/runtime dep
  • Prebuilt binary downloaders (node-pre-gyp, @mapbox/node-pre-gyp, prebuild-install, node-gyp-build in deps)
  • .node files in archive (prebuilt native addons)
  • WASM files in archive (.wasm)
  • napi-rs / @napi-rs/* in deps (Rust native addon intent)
  • optionalDependencies with os/cpu fields (platform-specific binary package pattern)
  • Platform/arch constraints on the package itself (os, cpu fields)
  • Executable bit set on files in tarball
  • Shell scripts in archive (shebang files)
  • files field omitted or overly broad (ships more than intended)

Go

  • go.mod present (module declaration)
  • replace directives in go.mod (non-standard source redirection)
  • Assembly files in module (.s presence)
  • C source files in module (.c, .h presence alongside Go — cgo indicator)
  • //go:build cgo or // +build cgo build tags (file-level, detectable from file listing with constraint parsing)
  • cmd/ directory structure (binary targets)
  • Main package indicators (file listing with likely main.go entries)

Rust (crates.io)

  • build.rs present in crate
  • links field in Cargo.toml (native library linkage)
  • -sys crate naming convention
  • [[bin]] targets declared
  • proc-macro = true in [lib] (compile-time execution in every dependent)
  • C/C++ source files shipped in crate (.c, .cpp, .h presence)
  • Build-dependencies on native-tool crates (cc, bindgen, cmake, pkg-config, cxx-build)
  • [package.metadata.docs.rs] features (informational)
  • Binary targets alongside library

Java / JVM (Maven, Gradle)

  • Native libraries in JAR (.so, .dylib, .dll, .jnilib entries)
  • Main-Class attribute in MANIFEST.MF
  • Service loader entries (META-INF/services/*)
  • Annotation processor declared (META-INF/services/javax.annotation.processing.Processor)
  • Maven plugin packaging (<packaging>maven-plugin</packaging>)
  • Gradle plugin markers (META-INF/gradle-plugins/*.properties)
  • Nested JARs (JAR contains .jar entries — shaded/fat indicator)
  • Native library loader hints (META-INF/native-image/)
  • Classpath entries in manifest (Class-Path attribute)

.NET (NuGet)

  • Install/uninstall PowerShell scripts (tools/install.ps1, uninstall.ps1, init.ps1)
  • MSBuild targets/props (build/*.targets, build/*.props, buildMultiTargeting/*)
  • Native binaries in runtimes/ folders (.so, .dylib, .dll)
  • tools/ folder presence (global tool or embedded executable)
  • contentFiles/ presence (copied into consumer projects)
  • analyzers/ folder presence (source generators / analyzers executing at compile time)
  • Package type (packageTypes in nuspec: Dependency, DotnetTool, Template)
  • Development dependency flag

PHP (Composer)

  • Scripts in composer.json (post-install-cmd, post-update-cmd, post-autoload-dump, etc.)
  • Custom installer type (type field ≠ library)
  • bin entries
  • PHP extension requirements (ext-* in require)
  • autoload.files (files executed on autoload, not just class resolution)
  • Composer plugin (type: composer-plugin — extends Composer itself)

Swift (SwiftPM)

  • System library targets declared
  • Binary targets (.binaryTarget with .xcframework or remote URL)
  • Plugin targets (.plugin — build tool plugins, command plugins)
  • C/C++/Objective-C target types (.target with C-family sources)
  • unsafeFlags usage in manifest
  • Executable targets (.executableTarget)

Elixir (Hex)

  • c_src/ directory presence (NIF indicator)
  • Makefile in package root
  • :elixir_make or :make as compiler in mix.exs
  • Rustler usage (rustler in deps, native/ directory)
  • Mix task modules (files under lib/mix/tasks/)
  • Escripts declared (escript config in mix.exs)
  • Applications list (:included_applications — auto-starts dependencies)

Haskell (Hackage)

  • build-type: Custom in .cabal (uses Setup.hs with arbitrary code)
  • C sources declared (c-sources, cxx-sources, include-dirs, extra-libraries)
  • Executable stanzas in .cabal
  • hs-source-dirs covering non-standard locations
  • Flag-driven conditional code (flag sections)

R (CRAN)

  • src/ directory present (compiled code)
  • configure / configure.win / cleanup scripts present
  • NeedsCompilation: yes in DESCRIPTION
  • SystemRequirements field (external native deps)
  • Makevars / Makevars.in / Makevars.win present
  • inst/ directory contents (arbitrary shipped files)

Dart / Flutter (pub.dev)

  • Platform plugin directories (ios/, android/, macos/, windows/, linux/ with native code)
  • build_runner / codegen deps declared
  • Native libraries per platform in archive
  • Executables declared (executables in pubspec)
  • Plugin declaration in pubspec (flutter.plugin section)
  • Platform constraints

OCaml (opam)

  • build: commands in opam file (arbitrary shell at build)
  • install: commands in opam file
  • remove: commands in opam file
  • C stubs declared (.c files in archive, dune stanzas with c_library_flags or foreign_stubs)
  • PPX preprocessor (ppx_* naming, kind: ppx_rewriter in dune-project)
  • External system deps (depexts: field)

Perl (CPAN)

  • Makefile.PL present (arbitrary Perl at build)
  • Build.PL present (Module::Build)
  • XS code (.xs files — C extension indicator)
  • META.json / META.yml with custom configure_requires
  • Executable scripts (bin/, script/ directories)

Lua (LuaRocks)

  • build.type in rockspec (builtin, make, cmake, command, module)
  • build.install.bin (installed executables)
  • C modules declared (build.modules with sources)
  • External dependencies (external_dependencies)

Julia (General registry)

  • deps/build.jl present (arbitrary code at build)
  • Binary artifacts via Artifacts.toml / JLLWrappers
  • Native library wrappers (_jll package naming)
  • Project.toml with [compat] pinning

Linux system packages (for comparison / SBOM context)

  • Maintainer scripts present (preinst, postinst, prerm, postrm, config)
  • Triggers declared
  • setuid/setgid files in package
  • systemd unit files shipped
  • File capabilities (getcap-visible) declared

Cross-cutting archive shape (applies to any ecosystem)

  • ELF binaries shipped (magic number check)
  • Mach-O binaries shipped (magic number check)
  • PE binaries shipped (magic number check)
  • Shared libraries by extension (.so, .dylib, .dll, .jnilib)
  • WASM modules (magic number \0asm)
  • Shell scripts (files with shebang — detectable from first bytes)
  • Files with executable bit set in archive
  • Symlinks in archive (especially relative paths escaping root)
  • Archive-in-archive (nested tarballs, zips, jars)
  • Files outside expected package layout for the ecosystem
  • Files above size threshold (binary blob heuristic)
  • Total archive size / file count outliers
  • Unusual file types for the ecosystem (e.g. .exe in a pure-Ruby gem)

Unified capability dimensions (cross-ecosystem rollup)

  • Executes code at install time (lifecycle scripts, opam install:, composer scripts, NuGet ps1)
  • Executes code at build time (build.rs, setup.py, Setup.hs custom, extconf.rb, c_src + Makefile)
  • Executes code in consumer's build (proc-macros, annotation processors, PPX, MSBuild targets, source generators, Mix tasks, Composer plugins, Gradle plugins)
  • Ships precompiled native binaries (wheel/gem platform tags, .node files, .so/.dylib/.dll in archive, NuGet runtimes/)
  • Registers shell commands (bin across ecosystems, executables, console_scripts)
  • Declares FFI intent (ffi gem, cffi/ctypes in Python, -sys crates, Go cgo, XS, NIFs)
  • Contains WASM
  • Platform-specific (non-portable)
  • Extends the consumer's build toolchain (plugins for the build system itself)
  • Ships executable files of any kind
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment