├── .github ├── dependabot.yml └── workflows │ └── watch.yml ├── Dockerfile ├── README.md ├── action.yml ├── entrypoint.sh ├── starter-workflow-psalm-security-scan.yml ├── taint-analysis-logo-light.svg └── taint-analysis-logo.svg /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | -------------------------------------------------------------------------------- /.github/workflows/watch.yml: -------------------------------------------------------------------------------- 1 | name: Watch 2 | 3 | on: 4 | schedule: 5 | - cron: '30 */4 * * *' 6 | push: 7 | branches: ['master'] 8 | 9 | jobs: 10 | docker: 11 | name: Push tagged docker image 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | packages: write 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | with: 21 | ref: master 22 | 23 | - name: Login to ghcr 24 | uses: docker/login-action@v3 25 | with: 26 | registry: ghcr.io 27 | username: ${{ github.repository_owner }} 28 | password: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | - name: Update Dockerfile with latest version 31 | id: fetch_version 32 | run: | 33 | last=$(curl -s https://packagist.org/packages/vimeo/psalm.json|jq '[.package.versions[]|select(.version|test("^\\d+\\.\\d+\\.\\d+$"))|.version]|max_by(.|[splits("[.]")]|map(tonumber))') 34 | last=$(echo $last | tr -d '"') 35 | echo "Last Psalm version is $last" 36 | echo "last=$last" >> $GITHUB_OUTPUT 37 | 38 | sed -i -re "s/require vimeo\/psalm/require vimeo\/psalm:$last/" Dockerfile 39 | cat Dockerfile 40 | 41 | - name: Build images 42 | run: docker build -t ghcr.io/psalm/psalm-security-scan:${{ steps.fetch_version.outputs.last }} -t ghcr.io/psalm/psalm-security-scan:latest . 43 | 44 | - name: Publish 45 | run: | 46 | docker push ghcr.io/psalm/psalm-security-scan:${{ steps.fetch_version.outputs.last }} 47 | docker push ghcr.io/psalm/psalm-security-scan:latest 48 | 49 | - name: Update action.yml 50 | run : | 51 | git config --global user.name "psalmbot" 52 | git config --global user.email "bot@noreply.psalm.dev" 53 | yq -i ".runs.image = \"docker://ghcr.io/psalm/psalm-security-scan:${{ steps.fetch_version.outputs.last }}\"" action.yml 54 | # Push commit when a file change has been made, skip if the commit would be empty 55 | git commit -m "Add newer psalm docker image version ${{ steps.fetch_version.outputs.last }}" action.yml && 56 | git push origin master || 57 | true 58 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-alpine 2 | 3 | LABEL "com.github.actions.name"="Psalm" 4 | LABEL "com.github.actions.description"="A static analysis tool for finding errors in PHP applications" 5 | LABEL "com.github.actions.icon"="check" 6 | LABEL "com.github.actions.color"="blue" 7 | 8 | LABEL "repository"="http://github.com/psalm/psalm-github-security-scan" 9 | LABEL "homepage"="http://github.com/actions" 10 | LABEL "maintainer"="Matt Brown " 11 | 12 | # Code borrowed from mickaelandrieu/psalm-ga which in turn borrowed from phpqa/psalm 13 | 14 | # Install Tini - https://github.com/krallin/tini 15 | 16 | RUN apk add --no-cache tini git 17 | 18 | COPY --from=composer:2.6.5 /usr/bin/composer /usr/bin/composer 19 | 20 | RUN COMPOSER_ALLOW_SUPERUSER=1 \ 21 | COMPOSER_HOME="/composer" \ 22 | composer global config minimum-stability dev 23 | 24 | # This line invalidates cache when master branch change 25 | ADD https://github.com/vimeo/psalm/commits/master.atom /dev/null 26 | 27 | RUN COMPOSER_ALLOW_SUPERUSER=1 \ 28 | COMPOSER_HOME="/composer" \ 29 | composer global require vimeo/psalm --prefer-dist --no-progress --dev 30 | 31 | ENV PATH /composer/vendor/bin:${PATH} 32 | 33 | # Satisfy Psalm's quest for a composer autoloader (with a symlink that disappears once a volume is mounted at /app) 34 | 35 | RUN mkdir /app && ln -s /composer/vendor/ /app/vendor 36 | 37 | # Add entrypoint script 38 | 39 | COPY ./entrypoint.sh /entrypoint.sh 40 | RUN chmod +x /entrypoint.sh 41 | 42 | # Package container 43 | 44 | WORKDIR "/app" 45 | ENTRYPOINT ["/entrypoint.sh"] 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Psalm Github Security Scan 2 | 3 | Run [Psalm’s Security Analysis](https://psalm.dev/docs/security_analysis/) as a Github action (a more general version [can be found here](https://github.com/psalm/psalm-github-actions)). 4 | 5 | ```yaml 6 | name: Psalm Security Scan 7 | 8 | on: [push, pull_request] 9 | 10 | jobs: 11 | psalm-security-scan: 12 | name: Psalm 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v3 17 | 18 | - name: Psalm Security Scan 19 | uses: docker://ghcr.io/psalm/psalm-security-scan 20 | 21 | - name: Import Security Analysis results into GitHub Security Code Scanning 22 | uses: github/codeql-action/upload-sarif@v2 23 | with: 24 | sarif_file: results.sarif 25 | ``` 26 | 27 | ## Specify Psalm version 28 | 29 | You can also specify a version. 30 | 31 | ```diff 32 | - uses: docker://ghcr.io/psalm/psalm-security-scan 33 | + uses: docker://ghcr.io/psalm/psalm-security-scan:5.7.7 34 | ``` 35 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | 2 | # https://help.github.com/en/articles/metadata-syntax-for-github-actions 3 | 4 | author: 'muglug' 5 | branding: 6 | icon: 'check' 7 | color: 'blue' 8 | name: 'Psalm – Security Scanner for PHP' 9 | description: 'Find security vulnerabilities in your PHP codebase with Psalm, a free and open-source tool created by Vimeo.' 10 | inputs: 11 | report_file: 12 | required: false 13 | default: 'results.sarif' 14 | description: 'File for Psalm’s output' 15 | runs: 16 | using: 'docker' 17 | image: 'docker://ghcr.io/psalm/psalm-security-scan:5.25.0' 18 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -l 2 | set -e 3 | 4 | REPORT_FILE="results.sarif" 5 | if [ ! -z "$INPUT_REPORT_FILE" ]; then 6 | REPORT_FILE="$INPUT_REPORT_FILE" 7 | fi 8 | 9 | ROOT=$PWD 10 | if [ ! -z "$INPUT_WORKING_DIR" ]; then 11 | cd "$INPUT_WORKING_DIR" 12 | fi 13 | 14 | if test -f "composer.json"; then 15 | COMPOSER_COMMAND="composer install --no-scripts --no-progress" 16 | echo "::group::$COMPOSER_COMMAND" 17 | $COMPOSER_COMMAND 18 | echo "::endgroup::" 19 | else 20 | echo "composer.json not found in repo, skipping Composer installation" 21 | fi 22 | 23 | /composer/vendor/bin/psalm --version 24 | /composer/vendor/bin/psalm --output-format=github --taint-analysis --report=$REPORT_FILE $* 25 | 26 | echo "sarif file saved to $PWD/$REPORT_FILE" 27 | 28 | cd $ROOT 29 | -------------------------------------------------------------------------------- /starter-workflow-psalm-security-scan.yml: -------------------------------------------------------------------------------- 1 | # For most projects this workflow file will not need changed at all - you 2 | # can just commit it to your repository. 3 | # 4 | # If you want to run Psalm *without* security scanning you should instead 5 | # use this GitHub Action: https://github.com/psalm/psalm-github-actions 6 | 7 | name: Psalm Security Scan 8 | 9 | on: [push, pull_request] 10 | 11 | jobs: 12 | psalm-security-scan: 13 | name: Psalm 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v3 18 | 19 | - name: Psalm Security Scan 20 | uses: docker://ghcr.io/psalm/psalm-security-scan 21 | 22 | - name: Import Security Analysis results into GitHub Security Code Scanning 23 | uses: github/codeql-action/upload-sarif@v2 24 | with: 25 | sarif_file: results.sarif 26 | -------------------------------------------------------------------------------- /taint-analysis-logo-light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /taint-analysis-logo.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------