Install-Time Lifecycle Script Introduction and Content Diff avatar

Install-Time Lifecycle Script Introduction and Content Diff

Pricing

from $15.00 / 1,000 run_starteds

Go to Apify Store
Install-Time Lifecycle Script Introduction and Content Diff

Install-Time Lifecycle Script Introduction and Content Diff

Compare the current and the proposed version of a dependency, and report every install-time script that the new version adds, removes or changes, with the flagged lines and a clean, review or block verdict.

Pricing

from $15.00 / 1,000 run_starteds

Rating

0.0

(0)

Developer

kingii98

kingii98

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Categories

Share

Install-Time Lifecycle Script Introduction and Content Diff Gate

Answer one question before you merge a dependency-bump pull request: does the new version run code at install time that the old version did not run, and what does that code do?

For each version pair, the Actor gets the old artifact and the new artifact from the public registry, reads the install-time scripts of both, and reports every script that the new version adds, removes or changes. A long-standing install script is not news, so only a difference is reported. Each added or changed script is scanned line by line for the shapes that an install-time attack needs, and the pair gets a clean, review or block verdict.

The Actor keeps no state between runs. It reads the old version from the registry inside the same run, so you keep no baseline.

What the Actor reads

EcosystemArtifactInstall-time surface
npmthe published .tgzthe preinstall, install, postinstall, prepare and prepublish commands in package.json, plus the local file each command names (for example install.js), plus an implicit node-gyp rebuild when the package holds binding.gyp and declares no install script
pypithe source distributionsetup.py, the file pip runs when it builds an sdist. A version that publishes wheels only runs no install-time code, and the Actor records that without a download

A script counts as changed when the command text changes or when the content of the file it names changes. That content diff is the point:

node install.js
can stay the same while install.js becomes something else.

Input

FieldTypeDefaultMeaning
bumpslist of stringsfour public example pairs1 to 200 version pairs, each ecosystem:name@from_version..to_version, for example npm:left-pad@1.1.3..1.3.0, npm:@scope/pkg@1.0.0..2.0.0 or pypi:psutil@5.9.7..5.9.8. An object with package, from_version and to_version is accepted too
flag_patternslist[]Extra texts to flag inside an added or changed script. Each text is a literal, case-insensitive substring, not a regular expression. Write {"name": "our_domain", "text": "x.test", "severity": "block"} to make one a block
max_artifact_mbinteger25The Actor skips a larger artifact, records the reason, and does not bill the pair

Every field has a schema default, so a run with empty input {} succeeds and reports the four example pairs.

The built-in flag patterns

PatternSeverityWhat it finds
shell_pipe_execblockdownloaded text piped into a shell or an interpreter
encoded_payloadblocka base64 or hex payload that the script decodes
dynamic_evalblockcode built at run time and evaluated
write_outside_packageblocka write outside the package directory, for example to the home directory or to cron
credential_accessblocka read of .npmrc, .ssh, AWS credentials or a token in the environment
network_fetchreviewa network call while the package installs
charcode_assemblyreviewtext built out of character codes
process_spawnreviewanother process started while the package installs

A line that only holds a comment runs nothing, so the scan steps over it.

Verdicts

VerdictWhen
cleanthe new version adds, removes and changes no install-time script
reviewa script is added, removed or changed, and nothing matches a block pattern; also a pair that could not be read, was skipped for size, or was written incorrectly
blockan added or changed script matches a block-severity pattern

A business verdict is never a failed run. A blocked bump, an unreachable registry, a malformed pair and a run with zero findings all end as a succeeded run that carries the verdict in the dataset and in the run status message.

Output

One dataset row for each version pair, plus one run-summary record.

Row fieldContract fieldMeaning
package, fromVersion, toVersionpackage, from_version, to_versionwhat was compared
scriptsBefore, scriptsAfterscripts_before, scripts_afterevery install-time script of each version, with its name, its command text, the file it names and that file's sha256
scriptsAdded, scriptsRemoved, scriptsChangedscripts_added, scripts_removed, scripts_changedthe difference, with the command and the file digest of both sides
flaggedPatternsflagged_patternsthe pattern name, the severity, the file, the line number and the matched line
artifactDigestBefore, artifactDigestAfterartifact_digest_before, artifact_digest_afterthe sha256 of the two artifacts the Actor read
verdict, blockReasonverdict, block_reasonclean, review or block, and the sentence that carries it

The summary record holds runVerdict, pairsRequested, pairsInspected, the verdict counts, the script counts, flaggedScriptCount, invalidCount and unreadableCount.

Billing (pay per event)

EventUnitCounted
run_startedone runonce for each run, before the work starts
version_pair_inspectedone version paironce for each unique version pair whose two artifacts the Actor read. A repeated pair is charged once. A malformed, skipped or unreachable pair is never charged
flagged_script_extractedone flagged scriptonce for each added or changed install-time script whose text matched a flag pattern. A clean bump list pays nothing for this event

Bounds and safety

  • HTTP only. No browser, no proxy, no key, no secret and no external database.
  • Three fixed public hosts: registry.npmjs.org, pypi.org and files.pythonhosted.org. A buyer string never becomes a host, a redirect is followed by hand at most twice, and only to a host in that list, so a private or reserved target cannot be reached.
  • 200 version pairs for each run, 4 downloads at a time, a 30 s timeout for each call, 25 MB for each artifact (64 MB limit), 1 MB for each file read out of an archive, and at most 12 files read out of one archive.
  • The Actor never unpacks a whole artifact. It reads the manifest and the few files the install scripts name. An absolute path, a parent-directory path and a member that declares more than the file limit are all refused.
  • A buyer-supplied flag pattern is a literal substring, so no pattern can make the scan slow.

Local use

uv sync
uv run pytest
uv run ruff check .
apify run # uses .actor/default_input.json