Guide 135 workflow

How to assess a malicious package warning

Assess a malicious package warning by separating listed, installed, executed, and observed access states, with containment and resumption tied to explicit evidence.

Treat a malicious package warning as a possible supply-chain incident, not an ordinary “upgrade when convenient” CVE. First validate the exact package, registry, version and source of the warning. Then determine whether that version was merely listed, downloaded, installed, or actually executed, and which credentials, files and network paths were available at the time. Pause new installs or builds that could repeat the exposure while the incident owner makes a scoped decision. Preserve available logs and artifacts as part of containment; avoid destructive cleanup where it would erase evidence, but do not delay urgent credential revocation or isolation to finish collection. If execution or access cannot be ruled out, respond to the potential compromise on that basis; a clean dependency scan after an update does not answer what happened earlier.

The answer changes with evidence. A lookalike package name in an unrelated ecosystem may be a false match. A package in a lockfile that no build installed is different from a package whose install script ran on a credentialed CI runner. Keep those states separate and date the conclusion.

Verify the warning before changing the tree

A “malware” label might come from a registry takedown, a reviewed advisory, a threat-intelligence feed, a heuristic detector or a typo in a ticket. Record the issuer, original URL, report time, package ecosystem and registry, exact name and affected versions. GitHub's advisory database documentation distinguishes malware advisories from ordinary vulnerability advisories with a type:malware filter. npm's malware reporting process says its security team validates reports, removes confirmed malicious packages and publishes an advisory; a third-party warning may precede or differ from that process.

Do not wait for a registry notice if credible evidence suggests an active compromise in your own environment. Conversely, do not label your system breached solely because a detector matched a string. The first task is to preserve a source-backed hypothesis that responders can investigate.

Work an invented warning through the first hour

Illustrative case; package, version, dates, organization and observations are invented: Harbor Billing receives a warning at 09:00 UTC that npm @harbor-demo/build-helper 1.3.0 may run a malicious postinstall script that attempts to send environment variables to an external endpoint. Harbor's production API image SBOM does not list the helper. Its release job lockfile lists it as a development dependency, and the CI logs show an npm install at 07:40. The team has not yet confirmed which exact package version that job fetched, whether scripts were enabled, or what credentials were present.

The tempting conclusion is “not in production, so no exposure.” That would miss the build runner. npm's lifecycle documentation describes install-time script stages, including postinstall. An install can execute package scripts before any application artifact is deployed. On the other hand, the mere presence of postinstall in a package's manifest does not prove it ran in Harbor's job. npm's ignore-scripts setting changes lifecycle-script execution; the team needs the npm version, effective configuration and logs for the affected run. It is not a general code-execution barrier: explicitly requested scripts such as npm run still run their main script, and a later application import can execute package code. Check those later steps even if installation scripts were disabled.

The security engineer gives the incident lead a short question set:

Question Harbor evidence at 09:15 Next evidence owner
Is this exact package/version covered by the warning? Warning names 1.3.0; lockfile entry may be 1.3.0, but artifact identity has not been checked. AppSec compares registry, integrity and affected versions.
Was it installed in the job? CI log shows install command; package-level result is not yet preserved. Build engineer exports install logs and job metadata.
Could a script run? Unknown effective npm config and script policy. Build engineer checks job config, command flags and install output.
What could the job access? Release runner may hold package-registry and artifact-publishing credentials. Platform owner scopes credential grants and exposure window.
Is there evidence of exfiltration? No confirmed evidence yet; network logs are incomplete. Incident response checks egress and host telemetry.
What is contained now? New release jobs using this lockfile are paused; existing production service remains up. Incident lead owns resumption decision.

This is an investigation, not a confirmed compromise. A missing egress log cannot prove that no data left; a recorded blocked connection is stronger but still requires checking other routes. A package's absence from one production image does not rule out exposure in a build runner, developer machine, batch job or old artifact.

Run a disciplined response sequence

1. Contain repetition without destroying evidence

Stop or isolate affected build jobs and prevent new resolution of the suspect version through the organization’s package proxy or equivalent control. Preserve the original advisory, lockfile, build logs, package integrity value, job configuration, runner identity, artifact digests and relevant network records. Coordinate with the incident owner before wiping a runner or deleting packages: those actions may remove evidence needed to determine what executed.

The exact containment depends on your environment. Pausing all delivery may be disproportionate if only a test-only job is in scope; continuing a credentialed release job with a credible malicious package warning may expand exposure. Write who decided, what was paused, and when it will be reviewed. NIST's incident response guidance treats response and recovery as coordinated risk-management work, rather than an unrecorded scanner cleanup.

2. Scope presence, installation and execution separately

Use the committed lockfile to identify the exact resolved package and its parent. Map every CI job, developer image, release artifact and runtime that used that lockfile during the affected interval. Then inspect installation records and effective script controls. Do not download and run a suspicious package on an analyst laptop “to see what it does.” Static inspection and controlled forensics belong in an isolated, authorized response workflow with evidence handling.

For Harbor, if the package was in the lockfile but no affected job installed it, the incident lead can narrow the execution hypothesis while still correcting the dependency tree. If the build job installed it with scripts enabled, assume the script could have executed until logs, policy and other evidence resolve the question. If telemetry shows the script process or a matching outbound request, record that as an observed event and expand the investigation.

3. Identify the reachable assets

List the runner's environment variables, files, mounted tokens, network access and role permissions at the time of execution. Separate available to the process from shown to have been read or sent. A credential available to an executed malicious script may need rapid revocation even without a complete exfiltration log; the incident lead should choose that action from the exposure and evidence, not wait for certainty that may never arrive. Scope downstream use of any artifact the job produced. If the job could publish releases, verify artifact integrity and publication history before trusting later builds.

Write the affected time window and the evidence gaps. If credentials are rotated, verify old ones are disabled and dependent systems use the new credentials. If a runner is rebuilt, verify the replacement's package controls and permissions. “We deleted node_modules” is not evidence that a token was never copied or that a published artifact is clean.

4. Replace the dependency and prove the path is gone

Remove or replace the malicious version in the direct or transitive tree; regenerate the lockfile under the team's normal controlled build process; review the changed dependency graph and package provenance. Use a known-good source of packages and avoid accepting a near-name substitute without scrutiny. Build a new artifact with appropriately restricted credentials and script policy. Confirm affected jobs no longer resolve the suspect version, then verify that the new artifact is the one deployed. Retain a separate incident record for the earlier interval, because a fixed future build does not erase past exposure.

If the report was a false match, keep the exact counterevidence: registry, package identity, version, integrity and why the warning did not apply. If the package was present but execution remains unknown, close the dependency removal work only; leave the incident scoping decision with the response owner.

Two evidence packets, two different resumption decisions

Continue the invented Harbor warning with two alternative packets. They are decision exercises, not forensic findings.

Packet A: listed but not installed in the affected run. The retained build input contains build-helper 1.3.0, but the effective install plan excludes that package, the package-level install inventory corroborates its absence, and the affected job never reaches a later command that loads it. This narrows the execution claim for that exact run. The incident lead can permit a corrected build input after review while checking other jobs that used the lockfile. “Not executed in run J71” does not become “never executed anywhere.”

Packet B: installed and executed with publishing authority. The package integrity matches the warning; the process record identifies its postinstall execution; the job's scoped credential record shows artifact-publishing permission. An outbound connection attempt is recorded as blocked, but other egress records are incomplete. Execution is supported. Successful exfiltration is not established, and one blocked connection does not settle it.

Decision Packet A Packet B
Future dependency use Remove suspect resolution and inspect replacement provenance Same, using a trusted clean build environment
Credential response Scope actual credential exposure; this run supplies counterevidence for package execution Incident owner revokes/replaces reachable publishing authority under response policy
Produced artifacts Assess this run's integrity from its actual execution evidence Hold trust in outputs the executed package could alter; inspect publication history and rebuild from trusted inputs
Resumption gate Corrected graph plus evidence this affected run did not execute the package Containment, credential handling, trusted replacement runner/build, and artifact disposition approved by incident owner
Historical conclusion No execution supported for J71 within the supplied evidence Execution supported; access/exfiltration scope remains under investigation

The strongest practical difference is artifact trust. Updating the dependency after Packet B does not make an artifact already produced by the affected job trustworthy. Conversely, destroying every artifact after Packet A without examining the execution boundary may be disproportionate.

A completed incident handoff for Packet B says: “The package's install script executed in J71 with artifact-publishing authority. We have not established exfiltration. New affected jobs are stopped, relevant authority is being replaced, and J71 outputs are held pending integrity review or trusted rebuild. Dependency removal and historical incident scoping have separate owners and closure evidence.” That is actionable without inventing a breach.

Give resumption a named scope

For Packet B, permission to resume a clean replacement build should name the job, input revision, credential arrangement and artifact disposition it covers. It is not permission to reuse the original runner, publish held outputs, or resume every job that shared the lockfile. Keep those decisions explicit when different owners control them.

The incident record should also preserve what remains unknown after delivery resumes. An organization may have contained repeat execution and rebuilt trusted outputs while the historical access investigation continues. That is a useful operational milestone, provided the record does not translate restored service into a claim that no earlier compromise occurred.

Next action: for the current warning, write the four states—listed, installed, executed, access or exfiltration observed—and attach one evidence item or “unknown” to each. If the package entered through another library, use the transitive dependency workflow for the replacement after containment. To check which release artifacts may contain it, use the deployment presence checklist.