Skip to content

# Sequence diagram for hardened npm publish workflow step #337

@Dargon789

Description

@Dargon789

Reviewer's Guide

Hardens the npm publish GitHub Actions workflow by validating matrix-derived package metadata and the computed package directory before invoking the publish script, mitigating artifact poisoning and path traversal risks.

Sequence diagram for hardened npm publish workflow step

sequenceDiagram
    actor Developer
    participant GitHubActions as GitHub_Actions_Workflow
    participant Job as Publish_Binary_Job
    participant Shell as Publish_Step_Shell
    participant FS as Filesystem_Artifacts
    participant Script as publish_mjs
    participant NPM as NPM_Registry

    Developer->>GitHubActions: Push tag / trigger release
    GitHubActions->>Job: Start matrix job (os, arch, tool)
    Job->>Shell: Run Publish_${os}-${arch}_Binary step

    Shell->>Shell: Set TOOL, PLATFORM, ARCH from matrix
    Shell->>Shell: Validate TOOL value (allowed chars)
    Shell->>Shell: Validate PLATFORM value (allowed chars)
    Shell->>Shell: Validate ARCH value (allowed chars)
    alt Invalid matrix value
        Shell-->>Job: Exit 1 (invalid TOOL/PLATFORM/ARCH)
        Job-->>GitHubActions: Mark job as failed
        GitHubActions-->>Developer: Report workflow failure
    else Matrix values valid
        Shell->>Shell: Compute PACKAGE_DIR
        Shell->>FS: Check PACKAGE_DIR exists and is directory
        alt PACKAGE_DIR missing
            Shell-->>Job: Exit 1 (missing PACKAGE_DIR)
            Job-->>GitHubActions: Mark job as failed
        else PACKAGE_DIR exists
            Shell->>FS: Resolve ABS_PACKAGE_DIR via realpath
            Shell->>FS: Resolve ABS_EXPECTED_ROOT for ./@foundry-rs
            Shell->>Shell: Verify ABS_PACKAGE_DIR prefix is ABS_EXPECTED_ROOT
            alt PACKAGE_DIR outside expected root
                Shell-->>Job: Exit 1 (path traversal detected)
                Job-->>GitHubActions: Mark job as failed
            else PACKAGE_DIR within expected root
                Shell->>FS: List contents of PACKAGE_DIR
                Shell->>FS: Check PACKAGE_DIR/package.json exists
                alt package.json missing
                    Shell-->>Job: Exit 1 (sanity check failed)
                    Job-->>GitHubActions: Mark job as failed
                else package.json present
                    Shell->>Script: bun ./scripts/publish.mjs PACKAGE_DIR
                    Script->>NPM: Publish package
                    NPM-->>Script: Publish result
                    Script-->>Shell: Exit status
                    Shell-->>Job: Complete step
                    Job-->>GitHubActions: Job successful
                    GitHubActions-->>Developer: Report publish success
                end
            end
        end
    end
Loading

Flow diagram for PACKAGE_DIR validation and publish logic

flowchart TD
    A[Start Publish Binary step] --> B[Set TOOL from matrix.tool]
    B --> C[Set PLATFORM from matrix.os]
    C --> D[Set ARCH from matrix.arch]

    D --> E{TOOL only has a-z A-Z 0-9 _ - and not empty}
    E -->|No| Z1[Fail workflow: invalid TOOL] --> Z[End]
    E -->|Yes| F{PLATFORM only has a-z A-Z 0-9 _ - and not empty}
    F -->|No| Z2[Fail workflow: invalid PLATFORM] --> Z
    F -->|Yes| G{ARCH only has a-z A-Z 0-9 _ - and not empty}
    G -->|No| Z3[Fail workflow: invalid ARCH] --> Z
    G -->|Yes| H[Compute PACKAGE_DIR = ./@foundry-rs/TOOL-PLATFORM-ARCH]

    H --> I{PACKAGE_DIR exists and is directory}
    I -->|No| Z4[Fail workflow: package directory does not exist] --> Z
    I -->|Yes| J[ABS_PACKAGE_DIR = realpath PACKAGE_DIR]

    J --> K[ABS_EXPECTED_ROOT = realpath ./@foundry-rs]
    K --> L{ABS_PACKAGE_DIR starts with ABS_EXPECTED_ROOT/}
    L -->|No| Z5[Fail workflow: directory outside expected root] --> Z
    L -->|Yes| M[List PACKAGE_DIR contents with ls -la]

    M --> N{PACKAGE_DIR/package.json exists}
    N -->|No| Z6[Fail workflow: package.json missing] --> Z
    N -->|Yes| O[Run bun ./scripts/publish.mjs PACKAGE_DIR]
    O --> P[Echo Published @foundry-rs/TOOL-PLATFORM-ARCH]
    P --> Z[End]
Loading

File-Level Changes

Change Details Files
Add defensive validation for TOOL, PLATFORM, and ARCH values in the npm publish workflow shell step to prevent malformed inputs and path manipulation.
  • Introduce case-based checks that ensure TOOL, PLATFORM, and ARCH are non-empty and contain only alphanumeric characters, dashes, or underscores
  • Fail the job early with clear error messages if any matrix-derived value does not meet the allowed pattern
.github/workflows/npm.yml
Validate the computed PACKAGE_DIR and its location before publishing to ensure artifacts remain within the trusted directory hierarchy.
  • Compute PACKAGE_DIR from TOOL, PLATFORM, and ARCH as before, but log its value for traceability
  • Check that PACKAGE_DIR exists and is a directory before proceeding
  • Resolve PACKAGE_DIR and the expected @foundry-rs root via realpath and verify that the package path is strictly under the expected root, rejecting path traversal or directory escape attempts
.github/workflows/npm.yml
Add a minimal content sanity check for the package directory before running the publish script.
  • Require that package.json exists inside PACKAGE_DIR before allowing publication
  • Abort the publish step with an explicit error if the expected file is missing, preventing attempts to publish malformed or incomplete artifacts
.github/workflows/npm.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Originally posted by @sourcery-ai[bot] in #336 (comment)

Metadata

Metadata

Assignees

Labels

P-highT-bugbugSomething isn't workingdependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationduplicateThis issue or pull request already existsenhancementNew feature or requestgithub_actionsPull requests that update GitHub Actions codegood first issueGood for newcomershelp wantedExtra attention is neededinvalidThis doesn't seem rightquestionFurther information is requestedrustPull requests that update rust codewontfixThis will not be worked on

Projects

Status

Backlog

Status

Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions