RepoCanaryScan a repo

What RepoCanary catches

Measured, not asserted. Every row on this page comes from running the real detection engine over a corpus of the techniques these campaigns use and recording which rules fire. Nothing is run, cloned, or installed: each sample is scanned as text, the same path a real scan uses.

Modelled on the documented playbooks of Contagious Interview, BeaverTail, InvisibleFerret, and OtterCookie, plus the wider npm and PyPI supply-chain scene.

101 / 101
techniques caught, 100% of the corpus
  • 69do not run
  • 32flagged
  • 0not caught
94
detection rules
exercised by the corpus
9
categories
one section each, below
0 / 71
honest repos called red
the standing benign set
6
known blind spots
listed at the end, not hidden

Rule chips are coloured by severity: high, medium, low. Any high fires red; mediums and lows accumulate toward a flag.

Nine ways a repository can be a trap

Pick a category to jump to its techniques and the rules that catch each one.

The full catalog

Npm install-time

8 techniques

Scripts that fire the moment npm install finishes, before you have read a line.

  • BeaverTail postinstall pipes a download into a shell

    Do not run
    lifecycle-scriptreadme-install-first
  • preinstall runs base64-decoded code with node -e

    Do not run
    lifecycle-script
  • postinstall runs a local file that fetches and runs code

    Do not run
    download-and-executelifecycle-script
  • postinstall hands a download to bash by process substitution, no pipe to match

    Do not run
    lifecycle-scriptreadme-install-first
  • prepare script (runs on git install) fetches and evals

    Do not run
    lifecycle-script
  • a plain npm script the README asks you to run pipes a download into a shell

    Flagged
    dangerous-npm-script
  • gypfile: true compiles and runs a native build during npm install

    Flagged
    native-build-at-install
  • a frontend app that also pulls in server and raw network packages

    Flagged
    odd-dependency-mix

Other ecosystems

15 techniques

The same trick in Python, Rust, Go, Gradle, Maven, PHP, Ruby, and notebooks.

  • Python setup.py shells out at install time

    Do not run
    download-and-executesetup-py-install-exec
  • Rust build.rs reaches for the network at compile time

    Do not run
    build-rs-network-execdownload-and-execute
  • Gradle build runs an external command

    Do not run
    gradle-build-exec
  • Maven exec plugin downloads and runs during build

    Do not run
    maven-build-exec
  • requirements.txt installs from a URL, not PyPI

    Flagged
    requirements-url-dependency
  • PHP composer post-install script downloads and runs code

    Do not run
    composer-install-script
  • Yarn Berry yarnPath points at a checked-in program, not the release bundle

    Do not run
    download-and-executeyarnrc-yarnpath
  • a Jupyter notebook code cell downloads and executes a payload

    Do not run
    download-and-executepython-download-execute
  • Ruby native-extension build (extconf.rb) shells out at gem install time

    Do not run
    download-and-executeruby-build-exec
  • a go:generate directive that downloads and runs a script

    Do not run
    download-and-executego-generate-exec
  • Gradle pulls dependencies from a plain-http repository

    Flagged
    gradle-insecure-repo
  • Maven pulls dependencies from a plain-http repository

    Flagged
    maven-insecure-repo
  • requirements.txt redirects pip to a private index

    Flagged
    requirements-custom-index
  • a Kotlin DSL Gradle build hands a download to a shell; commandLine("x") is the Kotlin spelling of commandLine 'x'

    Do not run
    gradle-build-exec
  • Yarn 1's .yarnrc points yarn-path at a checked-in program, the same hijack as Berry's yarnPath

    Do not run
    download-and-executeyarnrc-yarnpath

Dependency poisoning

12 techniques

A dependency that is not what the manifest says: git sources, tarballs, lookalike names, poisoned lockfiles.

  • lockfile resolves a familiar name to an attacker tarball

    Do not run
    lockfile-off-registry
  • npm alias protocol installs a different package under a trusted name

    Do not run
    npm-alias-mismatch
  • a scope one edit from @types

    Flagged
    scope-confusion
  • depends on a package confirmed malicious in these campaigns

    Do not run
    known-malicious-package
  • one-edit lookalike of a popular package, in a repository from a fresh account

    Flagged
    new-accounttyposquat-dependency
  • manifest asks for a version range, the lockfile resolves it from a git repository

    Do not run
    lockfile-git-dependencylockfile-manifest-mismatch
  • a dependency installed from a git source, not the registry

    Flagged
    manifest-non-registry-dependency
  • a deep dependency declares an install script

    Flagged
    transitive-install-script
  • committed .npmrc points npm at an attacker registry

    Do not run
    npmrc-registry-override
  • a committed .npmrc carries an auth token

    Flagged
    npmrc-committed-token
  • an npm override sends a trusted name to a tarball URL

    Flagged
    override-redirect
  • a committed .npmrc pointing npm at an unknown https registry with nothing else beside it; a company mirror and a hijack look the same, so it is a caution to read

    Flagged
    npmrc-registry-override

Obfuscation and loaders

12 techniques

Payloads hidden as encoded blobs, invisible characters, or two-stage loaders.

  • obfuscated eval(atob()) hidden in a build config

    Do not run
    dynamic-code-executioneval-decoded-blobhex-obfuscationpayload-in-config
  • new Function('require', fetchedCode) staged loader

    Do not run
    download-and-executedynamic-code-executionremote-code-execution
  • child_process assembled from string pieces to dodge scanners

    Do not run
    string-concat-api-hiding
  • a zero-width character splits an identifier

    Do not run
    invisible-characters
  • Unicode Private Use Area characters hide a payload as blank space

    Do not run
    private-use-steganography
  • bidirectional override characters make the source read differently than it runs

    Do not run
    bidi-override
  • a lookalike domain using a Cyrillic letter

    Do not run
    homoglyph-url
  • a large base64 blob beside the code that decodes it

    Do not run
    base64-blob
  • a loader parked after hundreds of spaces on a line that looks like a comment

    Do not run
    dynamic-code-executionreadme-install-firstwhitespace-hidden-code
  • a source file that is mostly hex data

    Flagged
    encoded-blob-density
  • the self-rotating string table of an automated JavaScript obfuscator

    Do not run
    hex-obfuscationobfuscator-io
  • text carried in Unicode tag characters, a block that renders as nothing at all

    Do not run
    unicode-tag-smuggling

Credential theft

7 techniques

Code that reads browser passwords, wallets, SSH keys, keychains, and environment secrets.

  • reads browser credential stores and a wallet extension by id

    Do not run
    lure-themewallet-extension-idwallet-file-access
  • reads SSH keys and cloud credential files

    Do not run
    wallet-file-access
  • reads the environment and posts it out

    Do not run
    env-exfiltration
  • walks the filesystem hunting for wallet and seed files

    Flagged
    stealer-file-globbing
  • pulls in keylogging and screen-capture components

    Flagged
    surveillance-dependency
  • osascript pops a fake system password dialog to phish the login password

    Do not run
    macos-password-phish
  • a keylogger and a screen-capture module wired into an app

    Flagged
    spyware-dependencysurveillance-dependency

Command and control

11 techniques

Beacons, dead drops, backdoors, and exfiltration to attacker infrastructure.

  • bare IP on a Contagious Interview C2 port

    Do not run
    c2-port-fingerprintdynamic-code-execution
  • calls documented BeaverTail command-and-control paths

    Do not run
    beavertail-c2-paths
  • InvisibleFerret remote-command handlers

    Do not run
    backdoor-command-handlers
  • fetches its next server address from a Google Doc dead drop

    Flagged
    dead-drop-resolver
  • posts collected data to a Discord webhook

    Flagged
    exfil-sink
  • opens a tunnel to reach the victim without a fixed server

    Flagged
    tunneling-infra
  • uploads collected data to an anonymous file host

    Flagged
    exfil-sink
  • silently installs a remote-desktop tool for unattended access

    Do not run
    remote-desktop-ratssh-backdoor
  • spawns PowerShell with a base64-encoded command to hide the payload

    Do not run
    download-and-executepowershell-encoded-command
  • uses certutil, a trusted Windows binary, to download the payload

    Do not run
    windows-lolbin-download
  • a disposable free-hosting endpoint on a campaign-style path

    Do not run
    beavertail-c2-pathsthrowaway-host-c2

Auto-run on open

30 techniques

Editor, AI-agent, container, make, and CI hooks that run with no install step at all.

  • VS Code task runs a command the moment the folder opens

    Do not run
    vscode-autorun-task
  • dev container hook downloads and runs code on open

    Do not run
    devcontainer-dangerous-hook
  • the same dev container hook behind a comment and a trailing comma; devcontainer.json is JSONC, so the tooling runs it either way

    Do not run
    devcontainer-dangerous-hook
  • a dev container configuration that cannot be parsed at all, so what it starts is unknown

    Flagged
    unparseable-autorun-config
  • Makefile target pipes a download into a shell

    Do not run
    makefile-remote-exec
  • pull_request_target runs untrusted code with secrets

    Flagged
    workflow-pwn-request
  • workflow curls repository secrets out

    Do not run
    workflow-secret-exfiltration
  • Dockerfile pipes a download into a shell during build

    Do not run
    dockerfile-remote-exec
  • Dockerfile saves a script on one line and runs it on a later one

    Do not run
    dockerfile-remote-exec
  • Makefile target evaluates a download as command text

    Do not run
    makefile-remote-exec
  • .pnpmfile.cjs runs code during pnpm install

    Do not run
    download-and-executepnpm-install-hook
  • VS Code settings point a linter at a checked-in binary run on folder open

    Do not run
    vscode-tool-path-hijack
  • .envrc runs on cd into the directory and downloads code

    Do not run
    direnv-envrc
  • docker compose service pipes a download into a shell

    Do not run
    compose-remote-exec
  • a dev container initializeCommand runs on the host, outside the container

    Flagged
    devcontainer-initialize-command
  • Dockerfile bakes a file straight from a URL into the image

    Flagged
    dockerfile-remote-add
  • a make target decodes base64 into a command

    Flagged
    makefile-obfuscated-command
  • workspace settings inject environment variables into every terminal

    Flagged
    vscode-terminal-env
  • a workflow that targets a self-hosted runner the candidate is asked to register

    Flagged
    workflow-self-hosted-runner
  • a checked-in MCP server the AI editor starts when the folder is opened

    Do not run
    download-and-executemcp-server-autostart
  • an agent hook runs a downloaded script while the coding agent works

    Do not run
    agent-hook-autorun
  • a .code-workspace file carries the tool-path hijack outside .vscode/

    Do not run
    vscode-tool-path-hijack
  • a dir-local eval form runs when a file in the directory is opened

    Do not run
    emacs-dir-locals-eval
  • a project-local Neovim config spawns a process when the folder is opened

    Do not run
    download-and-executeeditor-rc-autorun
  • an instruction hidden in an HTML comment, aimed at the coding agent and invisible to the reader

    Do not run
    agent-instruction-file
  • an instruction file that displaces the agent's own instructions and tells it to hide what it does

    Do not run
    agent-instruction-file
  • instructions painted out of sight, so the rendered file and the file the agent reads differ

    Do not run
    agent-instruction-file
  • a forged system turn inside the file an agent reads as instructions

    Do not run
    agent-instruction-file
  • a markdown image whose address carries a secret, so rendering it is the exfiltration

    Do not run
    agent-instruction-file
  • a folder-open task whose args name a checked-in program by a bare relative path, with the interpreter in command and the file in args

    Do not run
    dynamic-code-executionvscode-autorun-task

Manifest and persistence

3 techniques

Shadowed commands, shell-startup persistence, and sandbox checks before activating.

  • installs an executable that shadows the real npm command

    Do not run
    bin-command-shadowing
  • installs itself into a shell startup file to survive reboot

    Do not run
    lifecycle-scriptstartup-persistence
  • checks for a VM before activating (OtterCookie behavior)

    Flagged
    sandbox-evasion

Context signals

3 techniques

Account and README signals that raise suspicion but never convict on their own.

  • brand-new account, crypto theme, install-first README

    Flagged
    author-mismatchlure-themereadme-install-firstsingle-repo-account
  • README tells the victim to paste a shell command to fix a fake error

    Do not run
    readme-clickfix
  • a company organization with no public members

    Flagged
    empty-org

Documented blind spots

6 kept in the open

A tool that claims to catch everything is lying. These are techniques the static engine does not reliably catch, kept in the corpus and measured so the limit stays visible rather than quietly forgotten. This is why a green result never means safe.

Got a repo someone sent you?

Check it against every technique above before you run a single line, or before you open it in your editor.

Scan a repository