Package Security

EnhancedAdvanced

Overview

In Posit Package Manager, administrators have access to multiple tools to ensure good security practices across the organization. Package blocking allows administrators to block unwanted or malicious packages for both R and Python, restrict packages with specific licenses, and more. Known vulnerabilities for CRAN, Bioconductor, and PyPI packages are also reported via Package Manager.

Posit Package Manager does not run any security checks or vulnerability scanning. These actions are done upstream through OSV, see below for more information.

Vulnerability Reporting

Package Manager reports known security vulnerabilities with CRAN, Bioconductor, and PyPI packages from the Open Source Vulnerabilities (OSV) database.

These vulnerabilities, along with any associated NIST CVEs, will be displayed on the relevant package pages. Packages with vulnerabilities can also be blocked automatically. See below for more information.

The latest OSV vulnerability data is published to the Posit Package Service multiple times each day, and Package Manager will automatically synchronize this data in the background every 10 minutes.

For offline (“air-gapped”) environments, vulnerability data can also be synchronized via the offline downloader. See the rspm-offline-downloader get vulns command.

For more information about OSV data sources, such as the R Consortium Advisory Database or the PyPI Advisory Database, see the OSV data sources list.

Package Blocking

  • Packages can be blocked on a source-by-source basis or globally across entire instances to prevent packages from being downloaded.
  • When used alongside an approved set of packages, it can provide the tools for an effective defense-in-depth strategy.

Full package blocking support is available at the Advanced product tier. The Enhanced tier is limited to blocking only all known package vulnerabilities.

Blocklist Rules System

Package blocking utilizes an access-control list rules system to handle a variety of scenarios. For example, this system could:

  • Block a package by name.
  • Block all versions before a specific version of a package.
  • Only allow packages with a specific license when added to a source.

Translating rules to the command line is straightforward. Use the rspm create blocklist-rule command to create rules, and rspm list blocklist-rules command to list them.

Terminal
# Block the 'ggplot2' package globally from all sources
$ rspm create blocklist-rule --package-name=ggplot2

# Block all versions of the 'django' package earlier than version '4.0.0'.
$ rspm create blocklist-rule --package-name=django --version='<4.0.0'

# Block all 'AGPL' licensed packages globally, except for those in the 'exempt-source' source
$ rspm create blocklist-rule --license='AGPL'
$ rspm create blocklist-rule --license='AGPL' --source="exempt-source" --exception

# Block every VS Code extension published under the 'malicious-publisher' namespace
$ rspm create blocklist-rule --namespaces='malicious-publisher'

# Block every extension published under any of two namespaces in one rule
$ rspm create blocklist-rule --namespaces='publisher-a,publisher-b'

# Allow only one or more VS Code namespaces through an exception rule (must
# be paired with a broader block, such as a source-level rule on the openvsx
# source)
$ rspm create blocklist-rule --namespaces='ms-python' --exception
$ rspm create blocklist-rule --namespaces='ms-python,GitHub' --exception

# List all blocklist rules
$ rspm list blocklist-rules
Rules are printed in order of execution

ID: 1  Priority: 100
-- Match criteria:
 - Package name: ggplot2

ID: 2  Priority: 100
-- Match criteria:
 - Package name: django
 - Versions: <4.0.0

ID: 3  Priority: 100
-- Match criteria:
 - License: AGPL

Matching Criteria

Blocklist rules support several matching criteria. These include:

  • Vulnerabilities: Blocks CRAN, PyPI, and Bioconductor packages that have known vulnerabilities.
  • Minimum Severity: Blocks CRAN, PyPI, and Bioconductor packages with known vulnerabilities with a CVSS score at or above this threshold. Package Manager will pick the highest score available of all known vulns.
  • Block Unscored Vulnerabilities: Blocks CRAN, PyPI, and Bioconductor packages with known vulnerabilities that do not have a CVSS score. Defaults to true when using Minimum Severity.
  • Package name: Name of the package. R packages are case-sensitive, while Python packages are case-insensitive.
  • Namespaces: For Visual Studio Code extensions, a comma-separated list of publisher namespaces that Package Manager matches against the namespace portion of the extension ID. For example, ms-python matches ms-python.python and ms-python.vscode-pylance while ms-python,GitHub matches every extension under either publisher. Comparison is case-insensitive. Pair with --exception to allow only specific namespaces through a broader block. This criterion applies only to Visual Studio Extension (VSX) sources and repos. Package Manager does not support it for R, Python, or Bioconductor packages. Package Manager rejects rules that combine --namespaces with a non-VSX --source, a non-VSX --repo, or the --bioconductor flag.
  • License: Case-insensitive substring match of the package license name like MIT or GPL-3.
  • License types: A list of standardzed package licenses based on the SPDX License List, such as AGPL-3.0-or-later or CC-BY-4.0.
  • Version: A package version or a version specifier to match a range of versions. Version specifiers should be an operator followed by the version, such as <1.0.0. Valid operators include: >, >=, <, <=.
  • Source: Name of the source, such as cran or pypi. All source types are supported except Bioconductor, which must be blocked using a separate criteria for Bioconductor sources.
  • Repository: Name of the repository.
  • Deleted Packages: Block packages that have been removed from PyPI or CRAN from older snapshots. This is useful when a malicious package was removed from the upstream repository without making it onto a vulnerability list.
  • Minimum Age: Blocks package versions until they reach a minimum age, creating a cooldown on newly published versions. The age is expressed as a duration such as 30d or 24h. See below.

For more information on the matching criteria, the allowed operators, and licenses, reference the CLI appendix.

Important

Special characters such as <, >, $, \, *, =, and ! will be interpreted by your shell and require escaping.

In most shells, the easiest way to escape special characters is to surround them with single quotes ('). For example, if your version matching criteria is <4.0.0, you should run the command this way:

Terminal
rspm create blocklist-rule --package-name=django --version='<4.0.0'

Execution Order

When multiple rules are added, they are executed in a deterministic order. This order is as follows:

  1. Lowest priority rules are evaluated first (set via the --priority flag).
  2. Exceptions are evaluated before block rules with the same priority.
  3. If two rules conflict, the lower ID value is evaluated first.
  4. Rule evaluation for a package stops when a rule is matched, or when all rules have been evaluated.
Note

The default priority is 100.

Exempt Sources

One common pattern is to block all packages with a certain license, then allow exempt packages on an adhoc basis. This can be done using the --exception flag, as noted above.

The steps are generally:

  1. Block packages based off of the desired criteria (e.g., --license='AGPL').
  2. Create a curated or local source that will house the exempt packages.
  3. Configure an exception rule for the source.

This should allow for many possible configurations.

Testing the Rules

After adding blocklist rules, you can check whether a package is blocked in the Package Manager web UI. On the Packages page, blocked packages will be indicated with a “blocked” label next to the package version.

For more detailed testing, use the rspm test blocklist-rules command to test whether a package is either blocked or allowed by an exception rule. This command shows the specific rule that blocked or allowed the package and the total evaluation time.

When there are a large number of complicated rules, we recommend testing the rules with this command to ensure the behavior matches expectations.

Terminal
# Block all 'ggplot2' package versions newer than 1.0.0
$ rspm create blocklist-rule --package-name=ggplot2 --version='>1.0.0'
Rule successfully created.

ID: 10 Priority: 100
-- Match criteria:
 - Package name: ggplot2
 - Versions: >1.0.0

# Test the latest version of the 'ggplot2' package in the 'cran' repo
$ rspm test blocklist-rules --repo=cran --package-name=ggplot2
Evaluated 2 out of 10 total rules in 30µs

Blocked by rule ID: 10 Priority: 100
-- Match criteria:
 - Package name: ggplot2
 - Versions: >1.0.0

# Test an older version of the 'ggplot2' package, version 0.9.0, in the 'cran' repo
$ rspm test blocklist-rules --repo=cran --package-name=ggplot2 --version=0.9.0
Evaluated 10 out of 10 total rules in 200µs
No matching rules found

# Allow all 'django' package versions in the 'pypi' source
$ rspm create blocklist-rule --source=pypi --package-name=django --exception --description="Allow 'django' in PyPI"
Rule successfully created.

ID: 11 Priority: 100
-- Match criteria:
 - Package name: django
 - Source type: PyPI
-- Exception: yes
-- Description: Allow 'django' in PyPI

# Test that the latest version of 'django' in the 'pypi' repo was allowed
$ rspm test blocklist-rules --repo=pypi --package-name=django
Evaluated 1 out of 11 total rules in 40µs

Allowed by rule ID: 11  Priority: 100
-- Match criteria:
 - Package name: django
 - Source type: PyPI
-- Exception: yes
-- Description: Allow 'django' in PyPI
Note

Blocklist rules are evaluated during package download requests and could have performance overhead. The evaluation time shown indicates the amount of time the configured rules will add to these requests.

Blocking Packages by License

Packages can be blocked by license in two ways:

  • Using the --license flag, which takes a case-insensitive substring for matching package license fields.

    Terminal
    # Block all packages with 'AGPL' in their license field (case-insensitive)
    rspm create blocklist-rule --license='AGPL'
    
    # Block all packages with 'GNU Affero General Public License' in their license field
    rspm create blocklist-rule --license='GNU Affero General Public License'

    For example, --license='AGPL' will block licenses such as AGPL, AGPL (>= 3), or agpl | file LICENSE.

  • Using the --license-types flag, which takes a comma-separated list of standardized license identifiers in the SPDX License List, or Unknown for an unknown or missing license.

    Terminal
    # List all available license types for package blocking
    rspm list license-types
    
    # Block all known packages licensed under AGPL v3 or later
    rspm create blocklist-rule --license-types='AGPL-3.0-only,AGPL-3.0-or-later'
    
    # Block all packages with an unknown or missing license
    rspm create blocklist-rule --license-types='Unknown'

    Each language ecosystem has its own standard for specifying licenses, so Package Manager recognizes standard license types and allows you to block different variants of the same license more easily.

    For example, --license-types='AGPL-3.0-only,AGPL-3.0-or-later' will block all of the following packages with an AGPL v3 or AGPL v3+ license:

    • R package with license: AGPL-3, AGPL (>=3), AGPL + file LICENSE, or GNU Affero General Public License
    • Python package with license: GNU Affero General Public License v3 or later (AGPLv3+)

Recommendations:

  • Use --license-types to block specific licenses.
  • Use --license-types=Unknown to block all packages with non-standard or missing licenses.
  • Use --license to block specific license strings or non-standard licenses.
  • Use --exception to allow specific packages or sources that were inadvertently blocked.
Note

Currently, PyPI packages can only be blocked using the license of the latest package version. To block an old version of a PyPI package that has since changed its license, block by package name and version instead.

Note

Package Manager supports the PEP 639 License-Expression metadata field for Python packages. When a package provides a valid SPDX license expression, Package Manager parses it and uses it as the package license, taking precedence over the legacy classifier-based license detection. This applies only to package versions released on or after February 25, 2026.

Current limitations: Package Manager treats the OR operator in license expressions as AND and ignores the WITH exceptions operator.

Examples

  • Block all packages containing ‘AGPL’ in their license field:

    Terminal
    rspm create blocklist-rule --license='AGPL' \
        --description="Block all packages containing 'AGPL' in their license field"
  • Block all packages containing ‘GNU Affero General Public License’ in their license field:

    Terminal
    rspm create blocklist-rule \
        --license='GNU Affero General Public License' \
        --description="Block all packages containing 'GNU Affero General Public License' in their license field"
  • List available license types for blocking by license type:

    Terminal
    # List all license types
    rspm list license-types
    
    # List only current license types used by packages
    rspm list license-types --current
  • Block all known packages licensed under AGPL:

    Terminal
    rspm create blocklist-rule \
        --license-types='AGPL-1.0-only,AGPL-1.0-or-later,AGPL-3.0-only,AGPL-3.0-or-later' \
        --description='Block all packages licensed under AGPL'
  • Block all packages with an unknown or missing license:

    Terminal
    rspm create blocklist-rule --license-types='Unknown' \
        --description='Block all packages with an unknown or missing license'
  • Make an exception for the guesser PyPI package, which has an unknown license:

    Terminal
    rspm create blocklist-rule --source=pypi --package-name=guesser --exception \
        --description="Allow 'guesser' PyPI package with an unknown license"
  • Block all known packages licensed under common Creative Commons NonCommercial licenses:

    Terminal
    rspm create blocklist-rule \
        --license-types='CC-BY-NC-3.0,CC-BY-NC-4.0,CC-BY-NC-ND-4.0,CC-BY-NC-SA-3.0,CC-BY-NC-SA-4.0' \
        --description='Block all packages licensed under common CC-BY-NC licenses'
  • Only allow CRAN packages with an Apache 2.0 or MIT license:

    Terminal
    rspm create blocklist-rule --source=cran --description='Block all CRAN packages'
    
    rspm create blocklist-rule --source=cran --license-types='Apache-2.0,MIT' --exception \
        --description='Only allow CRAN packages with an Apache 2.0 or MIT license'

Blocking Packages with Vulnerabilities

At the Enhanced tier, only full system-wide vulnerability blocking is available via rspm create blocklist-rule --vulns. If enabled, all packages and versions with any known vulnerability will be blocked.

At the Advanced tier, full package blocking is available, including applying vulnerability blocking to specific sources or repositories, or making exceptions to allow specific vulnerable packages to be installed.

For finer grained control at the Advanced tier, rspm create blocklist-rule --min-severity=X.Y can be used. Package Manager will block a package if any of the vulnerabilities have a CVSS score at or above the given threshold. For vulnerabilities with multiple scores, the higher score will be used. Optionally, --block-unscored=true|false can also be used for vulnerabilities with no score; if left blank, the default value is true.

Note

All CRAN and Bioconductor vulnerabilities are currently unscored, as OSV does not provide a score for these ecosystems.

Examples

  • Block all packages with a Medium (5.0) or higher severity score AND unscored vulnerabilities

    Terminal
    rspm create blocklist-rule --min-severity=5.0 --description='Block all CRAN, Bioconductor, and PyPI packages with 5.0 or higher vuln score'
  • Block all packages with a Medium (5.0) or higher severity score but allow unscored vulnerabilities

    Terminal
    rspm create blocklist-rule --min-severity=5.0 --block-unscored=false --description='Block all CRAN, Bioconductor, and PyPI packages with 5.0 or higher vuln score, but allow unscored'

Blocking Newly Published Packages

A minimum-age rule blocks package versions until they have been published for a configured duration, creating a cooldown period before newly released versions can be installed. This helps guard against malicious or broken releases, which are often discovered and withdrawn shortly after publication.

Use the --min-age flag with a duration such as 30d (30 days) or 24h (24 hours). A version is blocked while its age is less than the configured minimum; once it is older than the minimum age, it is served normally.

Terminal
# Block any package version published within the last 30 days, across all sources
rspm create blocklist-rule --min-age=30d --description="Cooldown: block versions newer than 30 days"

# Apply a 7-day cooldown to a single source
rspm create blocklist-rule --source=pypi --min-age=7d --description="Cooldown for PyPI"

# Remove the blocklist rule
rspm delete blocklist-rule --id=1

A version’s age is measured from its publication date, which Package Manager determines based on the source type:

  • CRAN and Bioconductor sources (including Curated CRAN and CRAN Snapshot) use the date the version first became available in the repository’s snapshot timeline. Because snapshots are taken daily, this date can lag the upstream publication date by up to a day; for a cooldown that difference is conservative, since the version appears slightly younger and stays blocked slightly longer.
  • PyPI and Open VSX sources use the publication date recorded in the upstream manifest.
  • Git sources use the commit date of the tagged version or release that was built.
  • Local sources use the time the package was added to Package Manager (for R packages) or the package’s own upload or published timestamp (for Python and Open VSX packages).

If a version’s publication date cannot be determined — for example, an Open VSX extension uploaded without a published timestamp, or a CRAN version that predates the repository’s snapshot history — the rule does not apply to that version, and it is not blocked on the basis of age.

Note

A minimum-age rule is a more flexible alternative to the per-source DelaySyncDuration configuration option: it can be scoped to a specific package, source, or repository, and combined with --exception rules to allow specific packages through the cooldown.

Note

For PyPI repositories, a version that is still within its cooldown window is marked as “yanked” in the package’s simple index listing, so pip avoids selecting it unless that exact version is explicitly pinned, and a direct download of the version is rejected. Note, there might be a slight delay before the “yanked” marking of a package is no longer displayed once the package exits the cooldown window.

Editing Rules

Blocklist rules can be edited using the rspm edit blocklist-rule command, or deleted using the rspm delete blocklist-rule command.

Terminal
# List blocklist rules to find their IDs for editing
$ rspm list blocklist-rules
Rules are printed in order of execution

ID: 1 Priority: 100
-- Match criteria:
 - Package name: ggplot2

# Edit the version and description of the rule
$ rspm edit blocklist-rule --id=1 --version='<=3.3.2' --description='Block ggplot2 versions <=3.3.2'
Rule successfully edited.

ID: 1 Priority: 100
-- Match criteria:
 - Package name: ggplot2
 - Versions: <=3.3.2
-- Description: Block ggplot2 version <=3.3.2

# Delete the rule
$ rspm delete blocklist-rule --id=1
Rule successfully deleted.

Remote Usage

The blocklist commands can also be used remotely, e.g. rspm create blocklist-rule. This allows automating blocklist rule management or integrating with an external CVE database.

When generating a token to add blocklist rules remotely, the rspm create token command must be used with the --scope=blocklist:admin or --scope=blocklist:read flags. Admin scope gives full access to create, edit, delete, list, and test commands. Read scope gives access only to list and test commands.

For more information on remotely adding or editing blocklist rules, refer to the Admin CLI - Remote Use section.

Logging

When a user attempts to download a blocked package, Package Manager returns an HTTP 403 Forbidden response to the client.

Attempts to download a blocked package are also logged in the Package Service Log. Log entries for blocked downloads will contain a failed_service field value of service_error_blocked, along with a message field containing the ID of the blocking rule.

The Package Service Log is disabled by default. To enable it, set the Server.ServiceLog configuration property:

/etc/rstudio-pm/rstudio-pm.gcfg
[Server]
ServiceLog = "/var/log/rstudio/rstudio-pm/rstudio-pm.service.log"
Back to top