Skip to content

Process

Research Methodology

Every finding on this site moves through the same fixed process. Evidence is pinned to something immutable, the inclusion rule is decided before the count, an independent re-check is drawn from the result, and the limits are stated on the same page as the claim.

Five commitments

  1. No payload is run. No sample is executed as a whole and no attacker host is contacted. Analysis reads code, configuration and metadata; decoding and downloads run in an isolated virtual machine that cannot reach the host. One exception: to deobfuscate the Contagious Interview payloads, their string-decoder functions were evaluated on their own, with the network cut off.
  2. Pinned evidence. A claim about a repository names a commit; a claim about a file names its git object id; a claim about an archive names its SHA-256. Anyone can open the same object and see the same thing.
  3. Rules before counts. The inclusion rule is written down first, then applied to every candidate. Candidates the rule rejects are counted and shown by reason. If a check finds the rule too loose, the rule is tightened and the count is redone; the article says what that removed.
  4. Victims are not named. Owners of infected repositories are treated as compromised developers. Proof tables whose permalinks would identify them stay private and go to the platform, not to the public. Operator accounts are named only when an authenticated event ties them to the activity, never on a commit author name alone.
  5. Limits on the same page. What is not proven (execution, attribution, the final payload) is written next to what is, with the figures' history in a reconciliation table.

The process, step by step

Each step records something specific and refuses something specific. A finding that skips a step says so.

  1. 01 Discovery

    Start from an observed artefact: an antivirus alert, a suspicious file, a public report with loose ends. Widen with platform search (code, repository and commit search) and with pivots on what the artefact exposes: file hashes, hostnames, account identities, commit metadata.

    Recorded
    Every query, its date and its result count, so a number can be reproduced or explained later.
    Not done
    Treating a search hit as a finding. Search is a candidate generator with known gaps: default branches only, result caps, indexing delay.
  2. 02 Triage

    Sort candidates by what would have to be true for each one to be malicious, and by what the harmless explanation would look like: a test fixture, a security dataset, a detection rule quoting malware, a developer copying a template.

    Recorded
    The harmless explanations considered for each class, before anything is counted.
    Not done
    Severity. Triage decides what deserves evidence collection, not how bad it is.
  3. 03 Static analysis

    Read code and configuration without running it. Decode what is encoded (base64, hex, simple ciphers whose key sits beside the text), deobfuscate what can be deobfuscated statically, and map what the code would do: what it fetches, from where, what it reads, where it sends it.

    Recorded
    Hashes of every file analysed, the decoded values, and the exact step at which analysis stopped.
    Not done
    Execution of samples, contact with attacker infrastructure, opening links from suspicious content, and printing decoded payloads outside an isolated machine.
  4. 04 Evidence collection

    Pin every claim to something immutable. A repository is read by its numeric id, at a recorded commit; a file by its git object id and size; an account by its numeric id; an archive by its SHA-256. All network reads happen inside an isolated virtual machine with no access to the host.

    Recorded
    Repository id, owner id, commit, path, object id, rule name and a permalink to the file at that commit; for archives, the hash and the file list read in memory.
    Not done
    Storing file contents or victims' data. The record is enough to let anyone re-open the same file at the same commit.
  5. 05 Validation

    Fix the inclusion rule before counting, then re-read every candidate from scratch against that rule. Draw a stratified random sample with a recorded seed and re-read it through a second, independent API; every sample must match by object id.

    Recorded
    The rule text, the sample seed and size, the matches, and every candidate the rule rejected, grouped by reason.
    Not done
    Changing the rule after seeing the count. When a sample exposes a weak rule, the rule is tightened and the whole count is redone; the article says what that removed.
  6. 06 Infrastructure correlation

    Relate the artefacts to each other and to what is already public: shared hosts, shared files, shared identities in commit metadata, deployment records, registration data. Separate what the evidence shows (a push event by an authenticated account) from what can be set freely (an author name in a commit).

    Recorded
    Which link rests on which record, and which identities are known to be spoofed or reused.
    Not done
    Attribution. Where an activity is linked to a state actor, that link comes from named public reporting and is labelled as such.
  7. 07 Detection engineering

    Turn each inclusion rule into something a defender can run: YARA and Semgrep rules for source files, Sigma for logs where the behaviour leaves one, defanged indicators with context and confidence.

    Recorded
    For each rule run: the engine and version, the date, how many proven files it matched and missed, and which benign files it hit.
    Not done
    Shipping malware. Rules carry short pattern strings, never payloads.
  8. 08 Publication

    Notify the platform and the hosting providers first, with the full record, through their abuse channels. Publish only what is proven, with the rule, the count, the exclusions, the figures' history and the limits on the same page. Owners of infected repositories are victims and are never named. An account is named as an operator only when an authenticated event (a push, a repository it created or owns) ties it to the activity; a commit author name alone, which anyone can set, is not enough.

    Recorded
    A dated changelog of every figure that changed and why, kept on the page in a reconciliation table.
    Not done
    Publishing material that points readers at live stolen data, and publishing before the affected party has had the chance to act.

How counts are made

  • Repositories are counted by numeric id, accounts by numeric owner id. Renames and re-creations do not double count; one person holding several accounts counts as several accounts, and the text says so.
  • A repository counts once even when it carries two activities; per-activity rows can therefore add up to more than the total, and the page gives the overlap.
  • Forks are compared with their parent. A fork whose malicious file is byte-identical to the parent's is counted, and flagged as inherited, because removing the parent will not clean it.
  • Files under test, fixture, sample, corpus, dataset, documentation and research paths are excluded by rule, as are repositories whose only match is a security dataset or a detection rule quoting the pattern. Exceptions are listed one by one with the reason.
  • Platform search is a candidate source, not a count. Default branches only, capped result pages and indexing delay mean every total is a floor.
  • A counted set is frozen at a stated cut-off. What is found afterwards goes to a separate list with its own date and never silently changes a published figure.

Confidence levels

Confidence describes certainty, not severity. Severity is never assigned automatically, and a confidence label never upgrades itself: only new evidence moves a finding.

  • Confidence: Confirmed malicious

    The artefact itself proves it: a decoded payload, a loader read at a pinned commit, a hash known to be malware. No inference needed.

  • Confidence: High-confidence suspicious

    Every observable points one way and the harmless explanations were checked and rejected, but the final step (the payload, the execution, the operator) was not retrieved.

  • Confidence: Under investigation

    A real signal with an open question that the evidence collected so far cannot close. Stated so that the reader does not take it as settled.

  • Confidence: Previously documented

    Already described in public reporting. The research adds a count, a timeline or an artefact, not the finding itself.

  • Confidence: Unconfirmed

    A candidate that matched a pattern and nothing more. Listed only when its existence matters to the reader; never counted.

Disclosure

  • The hosting platform is notified first, through its abuse channel, with the complete record: ids, commits, hashes and the rule. Hosting and DNS providers of the attacker infrastructure are notified next. Each notification is logged with its date and ticket.
  • Follow-ups go to the same ticket. A list sent to a platform is frozen at a stated cut-off; later findings are sent as a separate, dated list, so the platform never receives a moving target.
  • Publication waits where reading the artefacts would expose victims (for example, repositories that publish stolen secrets) until the platform has removed them.
  • No executable malware is hosted. Indicators are defanged. Detection rules carry pattern strings, not payloads.

Corrections

Research is revised as evidence arrives. A changed figure is never overwritten silently: each article keeps a reconciliation table with the old and new values, the date and the reason, and the limitations section is updated with it. Mistakes found by re-checks are described as such, including what they removed from a count.

Tooling

Platform APIs (GitHub GraphQL and REST, registration and certificate-transparency data, public malware-hash services) read from an isolated virtual machine with scoped, short-lived tokens passed on standard input and never written to disk. Static decoding in Python inside that machine. Verification scripts, rule texts and query lists are kept with the evidence so that a count can be recomputed from the same inputs.