# The Open-Source Dependency Adoption Standard: Approve, Conditional, Reject

*You will grade any proposed open-source dependency as Approve, Conditional, or Reject against fixed criteria, so two engineers reach the same verdict and the record survives an audit.*

- Canonical URL: https://www.refolk.ai/guides/dependency-adoption-standard-2
- Pillar: Engineering and open source
- Format: Standard
- Published: 2026-10-14
- Last reviewed: 2026-10-14
- Reading time: 18 min

This is the standard for deciding whether a specific open-source package is cleared to enter your production codebase, and for leaving a record of why. It is written for engineering managers, technical founders, developer-relations leads, and technical sourcers who own dependency governance. By the end you will be able to grade any proposed dependency as Approve, Conditional, or Reject against fixed criteria, so two engineers reach the same verdict and the decision survives an audit.

Most public answers do one of two unhelpful things. They give you a generic "things to consider" checklist, or they hand you a single automated number and call it a day. This standard fixes pass/fail thresholds, ties them to a blast-radius tier, and produces a verdict plus the evidence an auditor asks for. The number is an input. The verdict is the output.

## What "cleared for production" actually means

Cleared for production means a named approver has graded the package against fixed criteria for its blast-radius tier and recorded the verdict, the evidence, and the date. It is a decision, not a reading.

The unit of decision is the verdict, and there are exactly three:

- **Approve.** The package passes every gate for its tier. It enters production with a recorded verdict.
- **Conditional.** The package passes the critical gates but has a specific, resolvable issue: a weak-copyleft license needing legal sign-off, a missing signed release, a pinning gap you will mitigate. Approval is contingent on the named condition being met and recorded.
- **Reject.** The package trips at least one hard gate: an exploitable workflow, a malicious install script, a blocking license for your distribution model, or an unresolved critical-check zero. No amount of composite score overrides a Reject trigger.

The reason this works as team policy is that the verdict is reproducible. Two engineers reading the same per-check scores, the same lockfile licenses, and the same install scripts should write down the same grade. If your process allows two honest engineers to disagree, it is not a standard yet.

> **Rule:** The verdict gates on individual checks, never the aggregate
>
> A single critical or high-weight check at 0 is a Reject or Conditional on its own, regardless of the composite score. The number is an input to the grade, not the grade.

## How to assign the blast-radius tier

Assign the tier by the package's runtime position, because review depth should be proportional to reach. The cleanest published model is three lanes.

The fast lane covers low-blast-radius updates with strong automated coverage and no production-facing behavior change, such as a development-only lint plugin. The standard lane covers most runtime dependencies, needing release notes, targeted tests, and normal deployment verification. The guarded lane covers anything that touches authentication, authorization, payments, persistence, messaging, traffic routing, encryption, deserialization, base images, or shared platform libraries.

One published scoring scheme assigns points by scope, which is useful when you want the tier to fall out of a rule rather than a judgement call:

| Scope | Points | Lane |
| --- | --- | --- |
| Runtime, production, direct, server, API | 3 | Guarded or standard |
| Transitive, indirect | 1.5 | Standard |
| Build, CI, tooling, lint, test | 1 | Fast |
| Development, optional | 0.5 | Fast |

One rule overrides the points: when a package requires installation-time execution, treat that as an elevated-risk capability, with review strength rising with installation-environment privilege. A dev-tool that runs a postinstall script on a developer's laptop with their credentials is not a fast-lane package.

#### Review depth follows blast radius

1. **Secrets or credential reachable** - Guarded lane: full gate set, named approver, legal review on any copyleft
2. **Production runtime** - Standard lane: Scorecard per-check, license on lockfile, install-script scan
3. **Build and CI tooling** - Fast lane plus install-time scrutiny, since scripts run with build privileges
4. **Development only** - Fast lane: automated coverage, lifecycle-script diff, lightweight record

*The deeper a package sits toward credentials and production traffic, the more gates it must clear.*

The tier precedes threshold application. Sources disagree on whether necessity or scope comes first in the overall procedure, but every sensible version assigns the tier before applying any pass/fail line, because the same Scorecard score means different things for a lint plugin and a crypto library.

## Reading OpenSSF Scorecard without being fooled by the number

Scorecard runs about 18 checks by default out of roughly 20 that exist, and each scores one security aspect from 0 to 10. The composite is a weighted average by risk label, and that weighting is exactly where the number lies to you.

Each check carries one of four risk weights: Critical is 10, High is 7.5, Medium is 5, and Low is 2.5. The composite averages those. That means a high-weight check sitting at 0 can disappear under a healthy-looking aggregate. The canonical example: a project might score 9 out of 10 overall but have a 0 on Branch-Protection, which is a critical gap regardless of the overall score.

So read these checks individually, and treat a 0 on any of them as a gate, not a data point:

| Check | Risk weight | Auto-flag meaning |
| --- | --- | --- |
| Dangerous-Workflow | 10 (Critical) | Exploitable workflow, fix or reject |
| Branch-Protection | 10 (High) | 0 means unreviewed merges possible |
| Maintained | 10 (High) | 0 means archived or inactive |
| Signed-Releases | 10 (High) | 0 means no artifact integrity |
| Vulnerabilities | 10 (High) | Open unfixed CVEs in OSV |

One quirk that trips people: the API returns -1 when a check could not be evaluated, for example when there is no release to inspect for Signed-Releases, or when it lacks the rights to read a setting. A -1 is not a zero. Do not penalize a repository for a check that could not run; confirm whether the underlying thing exists first.

**85% - npm packages found unmaintained in the Scorecard study**

More than 75% of PyPI packages were unmaintained too, which is why Maintained is a gate and not a nicety.

## Maintenance: abandoned, or just quiet?

A package fails the maintenance gate when no active maintainer would respond to a security report, not when it simply has few recent commits. Stable software does not need constant commits, and rejecting a mature, low-churn library is one of the most common false positives in this whole process.

Scorecard gives you a numeric line to start from. A project with at least one commit per week for the preceding 90 days, across its latest 30 commits and issues, earns the highest Maintained score, while an archived project earns the lowest. That turns "is it maintained?" from opinion into a checkable threshold.

But the threshold alone over-rejects. Abandonment is not the same as maliciousness, and a quiet package is only risky when its ecosystem changes or no one answers a security report. So pair the commit line with four context signals:

- **Recent releases** that track upstream or language changes.
- **Issue response**, even if slow, showing a human is reading.
- **Compatibility updates** after a major runtime or framework bump.
- **A named security contact** or security policy, so there is someone accountable.

If the commit count is low but issues get answered and a security contact exists, the package is quiet, not abandoned. If commits are low, issues rot, and no contact exists, it is abandoned, and that is a Conditional at best in the standard lane and a Reject in the guarded lane.

I ran this search: `Maintainers of popular npm packages who have published releases with provenance or signed releases in the last year.` - [see the full result list](https://www.refolk.ai/s/6v48t6q183).

*Returns named maintainers whose recent releases carry provenance or signatures, so you can confirm the human behind a package and check whether its publish-time trust level is intact.*

## License verification is a lockfile property, not a package property

Resolve the license of the package and every transitive dependency before you grade, because a clean top-level MIT license can mask a transitive GPL or AGPL that imposes obligations on your whole product. A single copyleft dependency anywhere in the tree can create viral obligations.

Map each resolved license to an action:

| License example | Category | Action |
| --- | --- | --- |
| MIT, Apache 2.0, BSD, ISC | Permissive | Approve, notice only |
| LGPL, MPL-2.0, EPL-2.0 | Weak copyleft | Conditional, legal review |
| GPL-2.0 or 3.0 | Strong copyleft | Block for distributed products |
| AGPL-3.0, SSPL | Network copyleft | Block for SaaS |
| BUSL-1.1 | Non-OSI source-available | Reject for production |

The mechanism matters because it determines whether a license even bites. GPL internal use and modification without distribution carry no obligation; distributing a binary or source triggers the source-provision requirement; and offering the software as a service is what triggers AGPL's network clause. Apache 2.0 adds explicit patent terms, which is why it clears cleanly for most commercial use. MPL is usable alongside proprietary code in separate files, but modifications to MPL-covered files must stay under MPL.

So the license verdict depends on your distribution model. A GPL library is fine for an internal tool you never ship and a blocker for a distributed binary. An AGPL dependency is the one that catches SaaS teams, because users interacting over a network is exactly the trigger. Non-OSI source-available terms like BUSL block production use without a separate license and are a straight Reject for your codebase until legal clears a commercial agreement.

> **Watch out:** Checking the manifest instead of the lockfile
>
> A top-level license scan reads clean while a transitive GPL or AGPL sits three levels down. Resolve licenses across the full resolved tree, not the declared dependencies, or your verdict is structurally unsound.

## Install-time and provenance: the gate a CVE scanner cannot be

Scan lifecycle scripts, git-URL sources, and provenance status as a gate separate from vulnerability scanning, because a malicious package has no CVE and a classic scanner matches packages against CVE databases. Malware passes a CVE-only pipeline by construction.

This is not a theoretical risk. When a developer runs an install, the package manager automatically executes preinstall, install, and postinstall lifecycle scripts with full user privileges before any explicit code invocation. Only 2.2% of packages declare such scripts, yet what those scripts execute propagates to every dependent project, and an average package implicitly trusts dozens of third-party packages downstream.

**2.2% - npm packages that declare install scripts**

A tiny surface that runs with full user privileges before any import, which is why ignore-scripts by default plus an allowlist beats per-package judgement.

The concrete signals that justify an automatic Reject:

- **Embedded shell commands in install scripts.** Researchers found 74 packages whose install scripts included keywords such as `curl`, `wget`, `/etc/shadow`, or `/etc/passwd`; 11 of those were malicious. Any of these in a lifecycle script is a Reject pending manual proof it is benign.
- **A dependency declared as a raw git URL.** A git-source dependency bypasses registry security controls entirely. Treat it as unvetted until it is pulled through the registry or explicitly reviewed.
- **A provenance or trust-level regression.** Some package managers now ship a trust policy that flags when a package's publish-time trust level has decreased, for example when a package previously published via a trusted publisher now ships without provenance or signatures. That regression is an early signal of account compromise and a hard flag.

The reason this is a standalone, non-optional criterion is that the dangerous moment is install, not import. A hijacked maintainer account can turn a package with 100M-plus weekly downloads into a malware channel through a postinstall-script dependency, and the first self-replicating npm worm used postinstall hooks to harvest npm, GitHub, and cloud credentials. In both cases there was no CVE to match. The install-script scan is the only gate that would have caught them.

> Malware passes a CVE-only pipeline by construction, so install-time behavior review is a separate gate, never a line item.

## The grading procedure

Run these steps in order, record the output of each, and the verdict falls out of the thresholds. The tier assigned in step two determines how strict the later gates are.

#### Grade a dependency Approve, Conditional, or Reject

1. **Justify necessity and verify identity** - Confirm the dependency cannot be avoided, then confirm you have the authentic package and not a fork or typosquat. Record the package URL and the confirmed source repository.
2. **Assign the blast-radius tier** - Classify by runtime position into dev-tool, production-runtime, or secrets-or-credential-reachable using the three-lane model, and record the tier before applying any threshold.
3. **Run Scorecard and read per-check scores** - Capture the composite plus Branch-Protection, Code-Review, Maintained, Signed-Releases, Dangerous-Workflow, Vulnerabilities, and Pinned-Dependencies individually, and attach the Scorecard JSON.
4. **Check maintenance signals** - Evaluate commit recency over 90 days, active maintainer count, and issue response, and write an abandoned-versus-quiet verdict with named evidence.
5. **Verify licenses against the lockfile** - Resolve the license of the package and every transitive dependency, flag weak, strong, and network copyleft and non-OSI terms, and set or clear the legal-review flag.
6. **Scan install-time and provenance** - Inspect lifecycle scripts, git-URL sources, and signature or provenance status against the auto-reject triggers, and record each trigger as checked.
7. **Grade and record the verdict** - Apply the fixed thresholds for the tier to produce Approve, Conditional, or Reject, and attach the record to the change ticket as CC8.1 evidence.
8. **Register for ongoing monitoring** - Add the package to continuous vulnerability alerting so new findings surface after adoption, satisfying CC7.1, and confirm it is in the monitored inventory.

#### Where candidate dependencies drop out

| Stage | Figure | Note |
| --- | --- | --- |
| Proposed dependencies | 100 | Requests entering the vetting queue |
| Pass identity and necessity | 80 | Avoidable or unverified packages removed |
| Pass per-check Scorecard gates | 55 | Critical and high-weight zeros removed |
| Pass license and install-time scan | 40 | Copyleft blockers and malicious scripts removed |
| Graded Approve or Conditional | 40 | Verdict recorded as change evidence |

*Each gate removes a share of packages, and the survivors carry a recorded verdict.*

### Fixed thresholds by tier

Apply these lines. They are the part you adopt as policy so that two engineers grade the same case the same way.

- **Any tier, hard Reject:** an exploitable Dangerous-Workflow, a malicious or unexplained install script matching the shell-command keywords, a raw git-URL source, a provenance regression, or a blocking license for your distribution model.
- **Fast lane (dev-tool):** Approve on passing the hard-Reject gates plus a lifecycle-script diff. A non-blocking license is fine. Maintenance can be quiet if a security contact exists.
- **Standard lane (production-runtime):** Approve requires no zero on any high-weight check, Maintained not abandoned, a non-copyleft or legal-cleared license, and no install-time flags. A missing Signed-Releases or a pinning gap drops it to Conditional with a named mitigation.
- **Guarded lane (secrets or credential reachable):** Approve requires all of the standard-lane gates plus Signed-Releases present, Branch-Protection and Code-Review above 0, and a named maintainer. Any weak-copyleft license requires legal sign-off before Approve.

## How this goes wrong

Most bad verdicts come from a handful of repeatable mistakes. Each has a false positive, and each has a specific check that prevents it. This is the most valuable part of the standard, because a reviewer who knows the failure modes grades consistently under pressure.

| Failure mode | What it looks like | The check that catches it |
| --- | --- | --- |
| Composite-score worship | "It scored 7-plus, ship it," over a 0 on Branch-Protection | Read per-check scores, never the aggregate alone |
| Treating -1 as a verdict | Penalizing a repo with no releases to inspect | Confirm whether a release exists before scoring |
| Confusing quiet with abandoned | Rejecting a mature, low-churn library | Look for issue response and a named security contact |
| License check on the manifest | A transitive GPL or AGPL hidden under clean top level | Resolve licenses across the full tree |
| CVE scanner as malware detector | A malicious package with no CVE passes clean | Add an install-script and behavior review |
| Self-reviewed change evidence | A CC8.1 trail an auditor reads as performative | Automated blocking gates plus retained approval records |
| Skipping install scripts because they are rare | Teams ignore the 2.2% that is the actual attack surface | Diff lifecycle scripts on every version bump |

Two of these deserve extra weight. The composite-score trap is the single most common way a dangerous package gets waved through, because the number looks authoritative and the gap is invisible unless you open the check. And the self-reviewed-evidence trap is where an otherwise-clean process fails the audit: SOC 2 CC8.1 does not strictly require a second human, but self-reviewed commits produce evidence that reads as performative. The approach that passes audits replaces the second human with automated gates, static analysis, dependency scanning, and secret detection that block deployment on critical findings, plus retained approval records.

> **Tip:** Encode the standard as gates when you have no dedicated reviewer
>
> In Refolk's index, the United States holds 1,159 AppSec, ProdSec, and supply-chain-titled engineers against 52 in Germany. If your team lacks a specialist, make the gates automated and blocking rather than relying on a human to remember them.

## The verdict record and the audit trail

A dependency is not cleared until the verdict, the evidence, and the date are recorded against the change. The record is what turns a judgement into something that survives an audit, and it is what satisfies two SOC 2 criteria at once.

CC8.1 governs the adoption as a change: every change to production follows a documented, approved process before it touches production. Typical CC8.1 evidence is a change ticket or pull request with approvals, security scan results attached to the change, written test sign-offs, and baseline configuration records. Your vetting record is exactly that evidence if you attach it to the ticket.

CC7.1 governs what happens after. It requires detection and monitoring procedures to identify newly discovered vulnerabilities in what you already run, which for dependencies means continuous monitoring, vulnerability alerting, and retained, timestamped scan evidence showing someone reviewed and acted on critical findings. That is why the final step registers the package for ongoing monitoring, and why findings must be fixed within SLA or formally accepted.

#### Before you call a dependency cleared

- [ ] Package URL and confirmed source repository are recorded, with typosquatting ruled out
- [ ] Blast-radius tier is assigned and written down before any threshold was applied
- [ ] Scorecard JSON is attached and the high-weight checks were read individually, with any -1 confirmed as not-run
- [ ] Maintenance verdict distinguishes abandoned from quiet, with issue-response or security-contact evidence
- [ ] Licenses are resolved across the full lockfile and the legal-review flag is set or cleared
- [ ] Lifecycle scripts, git-URL sources, and provenance status were scanned against the auto-reject triggers
- [ ] The Approve, Conditional, or Reject verdict is signed and attached to the change ticket as CC8.1 evidence
- [ ] The package is in the monitored inventory for continuous vulnerability alerting under CC7.1

Here is a verdict record skeleton you can paste into a ticket template so every vetting produces the same artifact.

**Dependency adoption verdict record**

```
Package (purl):
Source repo confirmed: yes / no
Blast-radius tier: fast / standard / guarded
Scorecard composite:
High-weight checks (0 = flag): Branch-Protection __ | Code-Review __ | Maintained __ | Signed-Releases __ | Dangerous-Workflow __ | Vulnerabilities __
Maintenance verdict: stable / quiet / abandoned  (evidence: __)
Licenses across lockfile: __   Legal-review flag: set / cleared
Install-time scan: scripts __ | git-URL deps __ | provenance __
Auto-reject triggers hit: none / listed: __
VERDICT: Approve / Conditional / Reject
Condition (if Conditional):
Approver: ____   Date: ____
Registered for monitoring: yes / no
```

*Paste into the change ticket; fill every field, and leave none blank, since a blank field is a gap an auditor will find.*

## Keeping the standard current

A dependency verdict is a snapshot, so the work is to keep the snapshot honest as the package changes. Re-grade on every version bump, because the install-script and provenance gates are the ones that regress when a maintainer account is compromised, and diffing lifecycle scripts on each upgrade is the single highest-value recurring check.

Re-check the thresholds themselves against the mechanism, not a fixed value, when the ecosystem moves. Scorecard's check set and default count can change, so confirm which checks run and their weights at the source rather than trusting a cached list. License outcomes depend on your distribution model, so when you move an internal tool to a distributed product or a SaaS, re-run the license gate, because a GPL or AGPL dependency that was harmless internally may now bite. When you need a dedicated owner for this process and do not have one, [Refolk](/) can surface named AppSec and supply-chain engineers from the public graph in plain English, so you can staff the gate instead of leaving it to whoever remembers. And keep the monitored inventory alive: CC7.1 is not a one-time scan but a standing obligation, and the package you cleared cleanly today is one hijacked release away from being the thing your alerting should have caught.

## Frequently asked questions

### How do I approve a new open source dependency so the decision survives an audit?

Grade it against fixed criteria rather than a vibe. Record the package URL and confirmed source, assign a blast-radius tier, run OpenSSF Scorecard and read the critical and high-weight checks individually, resolve licenses across the full lockfile, scan install-time scripts and provenance, then apply the tier thresholds to produce Approve, Conditional, or Reject. Attach that record to the change ticket. The retained verdict plus scan evidence is what satisfies SOC 2 CC8.1 change management at an audit.

### Is a package safe to use if it has a high OpenSSF Scorecard score?

Not necessarily. Scorecard weights only average, so a project can score 9 out of 10 overall and still score 0 on Branch-Protection, which is a critical gap regardless of the composite. Read the high-weight checks individually: Dangerous-Workflow, Branch-Protection, Maintained, Signed-Releases, and Vulnerabilities. A 0 on any of those is a flag the aggregate hides. Also note that a -1 means a check could not run, not a failure.

### What dependency red flags justify an automatic reject?

An exploitable Dangerous-Workflow, an install script that calls curl, wget, or touches /etc/shadow or /etc/passwd, a dependency declared as a raw git URL that bypasses registry controls, and a provenance regression where a package that previously shipped with signatures now ships without them. These are install-time and supply-chain signals that a CVE scanner cannot catch, because a malicious package has no CVE and runs with full privileges before any import.

### Do I need legal review for an MIT-licensed package?

Not for the top-level MIT license, but copyleft is a lockfile property, not a package property. A single transitive GPL or AGPL dependency can create viral obligations, and AGPL can oblige a SaaS operator to release source to network users. Always resolve licenses across the full dependency tree. Permissive licenses like MIT, Apache 2.0, BSD, and ISC clear on notice alone; weak copyleft needs legal review; strong and network copyleft block distributed and SaaS products respectively.

### How do I tell an abandoned package from one that is just quiet?

Scorecard gives a numeric line: at least one commit per week over the preceding 90 days earns the highest Maintained score, and an archived project earns the lowest. But quiet is not abandoned. Stable, low-churn software does not need constant commits. Pair the commit threshold with issue-response evidence, recent compatibility updates, and a named security contact. A package becomes genuinely risky when its ecosystem shifts or no active maintainer answers a security report.

---

*From the Refolk guide library. I revise these guides rather than replacing them, so the current version is always at https://www.refolk.ai/guides/dependency-adoption-standard-2*
