Install-Time Lifecycle Script Introduction and Content Diff
Pricing
from $15.00 / 1,000 run_starteds
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
Maintained by CommunityActor 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
| Ecosystem | Artifact | Install-time surface |
|---|---|---|
npm | the published .tgz | the 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 |
pypi | the source distribution | setup.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.jsinstall.js becomes something else.
Input
| Field | Type | Default | Meaning |
|---|---|---|---|
bumps | list of strings | four public example pairs | 1 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_patterns | list | [] | 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_mb | integer | 25 | The 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
| Pattern | Severity | What it finds |
|---|---|---|
shell_pipe_exec | block | downloaded text piped into a shell or an interpreter |
encoded_payload | block | a base64 or hex payload that the script decodes |
dynamic_eval | block | code built at run time and evaluated |
write_outside_package | block | a write outside the package directory, for example to the home directory or to cron |
credential_access | block | a read of .npmrc, .ssh, AWS credentials or a token in the environment |
network_fetch | review | a network call while the package installs |
charcode_assembly | review | text built out of character codes |
process_spawn | review | another process started while the package installs |
A line that only holds a comment runs nothing, so the scan steps over it.
Verdicts
| Verdict | When |
|---|---|
clean | the new version adds, removes and changes no install-time script |
review | a 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 |
block | an 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 field | Contract field | Meaning |
|---|---|---|
package, fromVersion, toVersion | package, from_version, to_version | what was compared |
scriptsBefore, scriptsAfter | scripts_before, scripts_after | every install-time script of each version, with its name, its command text, the file it names and that file's sha256 |
scriptsAdded, scriptsRemoved, scriptsChanged | scripts_added, scripts_removed, scripts_changed | the difference, with the command and the file digest of both sides |
flaggedPatterns | flagged_patterns | the pattern name, the severity, the file, the line number and the matched line |
artifactDigestBefore, artifactDigestAfter | artifact_digest_before, artifact_digest_after | the sha256 of the two artifacts the Actor read |
verdict, blockReason | verdict, block_reason | clean, 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)
| Event | Unit | Counted |
|---|---|---|
run_started | one run | once for each run, before the work starts |
version_pair_inspected | one version pair | once 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_extracted | one flagged script | once 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.organdfiles.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 syncuv run pytestuv run ruff check .apify run # uses .actor/default_input.json