-
Notifications
You must be signed in to change notification settings - Fork 1
move npm-security container from private repo #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| name: Verify NPM Security | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| working-directory: | ||
| description: "Working directory" | ||
| required: true | ||
| type: string | ||
|
|
||
| jobs: | ||
| verify-npm-install: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| packages: read | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v5 | ||
|
|
||
| - name: Log in to GitHub Container Registry | ||
| run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin | ||
|
|
||
| - name: Pull container | ||
| run: docker pull ghcr.io/fundwave/npm-security:latest | ||
|
|
||
| - name: Verify npm install | ||
| working-directory: ${{ inputs.working-directory }} | ||
| run: | | ||
| docker run --rm \ | ||
| -v $(pwd):/app \ | ||
| -e GITHUB_READ_TOKEN=${{ secrets.GITHUB_TOKEN }} \ | ||
| ghcr.io/fundwave/npm-security:latest | ||
|
|
||
| - name: Verification complete | ||
| run: echo "NPM dependencies verified successfully in ${{ inputs.working-directory }}" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| //npm.pkg.github.com/:_authToken=${GITHUB_READ_TOKEN} | ||
| @fundwave:registry=https://npm.pkg.github.com | ||
| legacy-peer-deps=true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| FROM node:22-alpine | ||
|
|
||
| RUN mkdir -p /home/node/test | ||
| RUN chown -R node:node /home/node/test | ||
|
|
||
| WORKDIR /home/node/test | ||
|
|
||
| COPY .npmrc /home/node/.npmrc | ||
| COPY entrypoint.sh /entrypoint.sh | ||
| RUN chmod +x /entrypoint.sh | ||
|
|
||
| USER node | ||
|
|
||
| ENTRYPOINT ["sh", "/entrypoint.sh"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| WARNING: Only use read-only GITHUB token as GITHUB_READ_TOKEN. If a WRITE token is supplied, the container will cause malicious packages to be published. | ||
|
|
||
| # NPM Security Container | ||
|
|
||
| A Docker container designed to securely install npm dependencies and perform integrity checks on JavaScript files looking specifically for hashes that indicate infiltration by Sha1-Hulud malware that came out on Nov 24 2025. This container can be used as a precautionary step before `npm install`. | ||
|
|
||
| ## Overview | ||
|
|
||
| This container performs the following operations: | ||
| 1. Sets up authentication for GitHub packages (if `GITHUB_READ_TOKEN` is provided) | ||
| 2. Runs `npm install` to install dependencies | ||
| 3. Performs integrity checks on JavaScript files using SHA256 hashes that point to Sha1-Hulud malware files. | ||
| 4. Returns appropriate exit codes based on the results | ||
|
|
||
| ## Exit Codes | ||
|
|
||
| The container uses specific exit codes to indicate different states: | ||
|
|
||
| - **Exit Code 0**: ✅ **Success** - npm install succeeded and all integrity checks passed | ||
| - **Exit Code 1**: ❌ **Security Alert** - Integrity check failed, potentially malicious files detected | ||
| - **Exit Code 2**: 🔧 **Installation Error** - npm install failed | ||
|
|
||
| ## Usage | ||
|
|
||
| ### Basic Usage | ||
|
|
||
| ```bash | ||
| docker pull ghcr.io/fundwave/npm-security:latest | ||
| docker run --rm -v $(pwd):/app ghcr.io/fundwave/npm-security:latest | ||
| ``` | ||
|
|
||
| ### With GitHub Read Token (for private packages) | ||
|
|
||
| ```bash | ||
| docker run --rm -v $(pwd):/app -e GITHUB_READ_TOKEN=your_github_readonly_token ghcr.io/fundwave/npm-security:latest | ||
| ``` | ||
|
|
||
| ### CI/CD Integration | ||
|
|
||
| In your CI/CD pipeline, you can use the exit codes to determine the next steps: | ||
|
|
||
| ```yaml | ||
| # Example GitHub Actions workflow | ||
| - name: Secure NPM Install | ||
| run: | | ||
| docker run --rm -v $(pwd):/app -e GITHUB_READ_TOKEN=${{ secrets.GITHUB_TOKEN }} ghcr.io/fundwave/npm-security:latest | ||
| EXIT_CODE=$? | ||
| if [ $EXIT_CODE -eq 0 ]; then | ||
| echo "Proceeding with build..." | ||
| elif [ $EXIT_CODE -eq 1 ]; then | ||
| echo "Security check failed - stopping deployment" | ||
| exit 1 | ||
| elif [ $EXIT_CODE -eq 2 ]; then | ||
| echo "Installation failed - check dependencies" | ||
| exit 1 | ||
| fi | ||
| ``` | ||
|
|
||
| ## Security Features | ||
|
|
||
| ### Integrity Checking | ||
|
|
||
| The container checks for known malicious file hashes: | ||
| - Scans all `.js` files in the project | ||
| - Compares SHA256 hashes against a database of known threats | ||
| - Fails if any malicious patterns are detected | ||
|
|
||
| ### Isolated Environment | ||
|
|
||
| - Runs in a containerized environment | ||
| - No access to host system beyond mounted volume | ||
| - Clean Alpine Linux base with minimal attack surface | ||
| - Use only a read-only GITHUB_TOKEN (with package read permissions only). Do not use tokens with write or publish permissions, as this may allow malicious packages to be published. | ||
|
|
||
| ### Authentication | ||
|
|
||
| - Supports GitHub Package Registry authentication | ||
| - Tokens are handled securely through environment variables | ||
| - No credentials stored in the container image | ||
|
|
||
| ## Development | ||
|
|
||
| ### Building the Container | ||
|
|
||
| ```bash | ||
| docker build -t npm-security . | ||
| ``` | ||
|
|
||
| ### Testing | ||
|
|
||
| Test with a clean Node.js project: | ||
|
|
||
| ```bash | ||
| # Create test project | ||
| mkdir test-project && cd test-project | ||
| npm init -y | ||
| echo '{}' > package.json | ||
|
|
||
| # Test the container | ||
| docker run --rm -v $(pwd):/app npm-security | ||
| echo "Exit code: $?" | ||
| ``` | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| - `GITHUB_READ_TOKEN`: GitHub Personal Access Token for accessing private packages in GitHub Package Registry | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Common Issues | ||
|
|
||
| 1. **Permission denied**: Ensure the mounted volume has correct permissions | ||
| 2. **Network issues**: Check Docker network configuration for npm registry access | ||
| 3. **Token issues**: Verify GitHub token has correct permissions for package registry | ||
|
|
||
| ### Debug Mode | ||
|
|
||
| Run the container interactively to debug issues: | ||
|
|
||
| ```bash | ||
| docker run -it --rm -v $(pwd):/app --entrypoint sh npm-security | ||
| ``` | ||
|
|
||
| ## Contributing | ||
|
|
||
| When modifying the integrity check hashes, ensure you're adding legitimate threat signatures and document the source of the hash information. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,60 @@ | ||||||||||||||||||||
| #!/bin/sh | ||||||||||||||||||||
| set -e | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Known malicious SHA256 hashes (Sha1-Hulud malware signatures) | ||||||||||||||||||||
| MALICIOUS_HASHES="a3894003ad1d293ba96d77881ccd2071446dc3f65f434669b49b3da92421901a|\ | ||||||||||||||||||||
| 62ee164b9b306250c1172583f138c9614139264f889fa99614903c12755468d0|\ | ||||||||||||||||||||
| c723605455e8667a4c84327cf6b704bbdcb9b4ce3707ddddd927d32b8372ff77|\ | ||||||||||||||||||||
| 2e44e8d8a8e906fd5bfbb37be08dfe2dcf1ce41bd4ba726987ab516446dfb4f1|\ | ||||||||||||||||||||
| fa7df9e9fc5390cc54e0086073fc9b3054087ffddf661bbc9f836b007fa25f20|\ | ||||||||||||||||||||
| d66343059793800e72ef17690ce26492dc854c8513905778630ff1ed4e7a81b8|\ | ||||||||||||||||||||
| 981d3e2f5d7e26c93bd4b758ea722468900894fb2368db5f8399282e2414fe33" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Exit codes | ||||||||||||||||||||
| EXIT_SUCCESS=0 | ||||||||||||||||||||
| EXIT_SECURITY_ALERT=1 | ||||||||||||||||||||
| EXIT_INSTALLATION_ERROR=2 | ||||||||||||||||||||
|
|
||||||||||||||||||||
| echo "Validating GitHub Token..." | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if [ -f /app/package.json ] ; then cp /app/package.json ~/test/package.json; fi | ||||||||||||||||||||
| if [ -f /app/package-lock.json ] ; then cp /app/package-lock.json ~/test/package-lock.json; fi | ||||||||||||||||||||
| if [ -f /.env ] ; then echo ".env file found. Exiting"; exit $EXIT_INSTALLATION_ERROR; fi | ||||||||||||||||||||
|
||||||||||||||||||||
| if [ -f /.env ] ; then echo ".env file found. Exiting"; exit $EXIT_INSTALLATION_ERROR; fi | |
| if [ -f /app/.env ] ; then echo ".env file found. Exiting"; exit $EXIT_INSTALLATION_ERROR; fi |
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The suspicious files output should be formatted for better readability and actionability. Consider iterating over the results to show the file path and hash separately, making it easier to identify which specific files triggered the alert.
| echo "$SUSPICIOUS_FILES" | |
| echo "The following suspicious files were found:" | |
| echo "$SUSPICIOUS_FILES" | while read -r line; do | |
| HASH=$(echo "$line" | awk '{print $1}') | |
| FILE=$(echo "$line" | awk '{print $2}') | |
| echo " File: $FILE" | |
| echo " Hash: $HASH" | |
| echo "-----------------------------" | |
| done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The working directory is set to
/home/node/testin the Dockerfile, but these commands copy to~/test/. Since the script runs as the 'node' user,~expands to/home/node/, making the destination/home/node/test/. However, the source path/app/doesn't align with the/home/node/testworking directory. The volume mount is expected at/app, but the script should work within the current directory. Consider usingcp /app/package*.json .or clarifying the directory structure.