Skip to content

Package Blocking#

Advanced

Overview#

In Posit Package manager, package blocking allows administrators to block unwanted or malicious packages, restrict packages with specific licenses, and more.

  • 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.

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

# 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:

  • Package name: Name of the package. R packages are case-sensitive, while Python packages are case-insensitive.
  • 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.
  • Vulnerabilities: Blocks package versions that have known vulnerabilities.
  • 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.

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.

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'
    

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#

Attempts to download a blocked package are 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"