Socket in your Gitlab Pipeline

Socket fights vulnerabilities and provides visibility, defense-in-depth, and proactive supply chain protection for your open source dependencies. It is easy to integrate Socket into your Gitlab Pipeline to provide an extra layer of security against Supply Chain Attacks.

Adding Socket to your pipeline

Create your Socket API Key

You can either create your API Key yourself if you have permissions in your socket.dev account or you can have your Admin create it for you with the Reports scope.

  1. Log into the socket.dev dashboard

  2. Go to Settings

  3. Go to the API Tokens tab

  4. Select Create API Token

  5. Give the token a name like Gitlab API Token

  6. Select the report Scope

  7. Click Confirm

  8. Click on Show key

  9. Click on the API Token to copy

Get your Socket Org ID

  1. Go to https://docs.socket.dev/reference/getorganizations

  2. In the username portion of the tester put in your Socker API Key

  3. Click try it

  4. Save the ID from the response

Create your Socket Environment variables

  1. Log into Gitlab

  2. Navigate to your project

  3. Go to Settings -> CI/CD

  4. Expand Variables

  5. Do Add Variable

    1. Select Mask Variable

    2. Key: SOCKET_SECURITY_API_KEY

    3. Value: Socket API Token

  6. Click Add variable

  7. Add another variable

    1. Key: SOCKET_ORG_ID
    2. Value: org_id
  8. If you would like comments about security findings on an unhealthy report you will need a gitlab access token

    1. Key: GITLAB_TOKEN
    2. Value: gitlab_token_with_access

Example Gitlab Pipeline Setup

  1. Go to Build

  2. Go to Pipeline Editor

  3. Paste the following Pipeline Yaml, or integrate with your existing code

    test:
      image: node:latest
      rules:
          - if: $CI_MERGE_REQUEST_ID
            when: always
            changes:
            - package.json
            - package-lock.json
          - when: never
      before_script:
      - apt-get -qq update
      - apt-get install -y jq
      script:
          - npm install -g @socketsecurity/cli
          - |
            SOCKET_SETTINGS_FILE=socket_settings.json
            SHOW_ALL=0
            # base64 encode API Token
            SOCKET_TOKEN=$(printf "$SOCKET_SECURITY_API_KEY:" | base64)
    
            # Get the Org Settings
            curl -s --location 'https://api.socket.dev/v0/settings' \
            --header 'Content-Type: application/json' \
            --header "Authorization: Basic $SOCKET_TOKEN" \
            --data '[{"organization": "'$SOCKET_ORG_ID'"}]' > $SOCKET_SETTINGS_FILE
    
            # Org settings parse
            echo "Getting Security Policy"
            ORG_SETTINGS=$(cat $SOCKET_SETTINGS_FILE | jq -r ' .entries[].settings.organization as $top | $top | .issueRules | keys | .[] as $issue | $top | .issueRules[$issue].action as $action | $issue + "=" + $action' | sort)
            DEFAULT_SETTINGS=$(cat $SOCKET_SETTINGS_FILE | jq -r ' .defaults as $top | $top | .issueRules | keys | .[] as $issue | $top | .issueRules[$issue].action as $action | $issue + "=" + $action' | sort)
            # Clean up settings file
            rm $SOCKET_SETTINGS_FILE
            set --
            for SETTING in $DEFAULT_SETTINGS; do
              if [[ $ORG_SETTINGS == *"$SETTING"* ]]; then
                echo "$SETTING set at org level" > /dev/null
              else
                set -- "$@" $SETTING
              fi
            done
    
            for SETTING in $ORG_SETTINGS; do
              set -- "$@" $SETTING
            done
    
            # Report Parse
            # Commented out one is to test with saved output
            # REPORT_RESULT=$(cat output.json | jq -r '("scan_passed=" + (if .healthy then "true" else "false" end) ),(.issues[] | .type as $type | .value | .severity as $severity | .category as $category | .locations[] | .type as $ecosystem | .value | (.package + "@" + .version) as $package | $severity + "," + $category + "," + $type + "," + $ecosystem + "," + $package)')
            echo "Running Scan"
            REPORT_RESULT=$(socket ci --all --json . 2>&1 | tail +5 | jq -r '("scan_passed=" + (if .healthy then "true" else "false" end) ),(.issues[] | .type as $type | .value | .severity as $severity | .category as $category | .locations[] | .type as $ecosystem | .value | (.package + "@" + .version) as $package | $severity + "," + $category + "," + $type + "," + $ecosystem + "," + $package)')
            SCAN_PASSED=0
            for RESULT in $REPORT_RESULT; do
              
              if [[ $RESULT == *"scan_passed"* ]]; then
                echo $RESULT > /dev/null
                SCAN_RESULT=$(echo $RESULT | awk 'BEGIN{FS="="}{print $2}')
                if [ $SCAN_RESULT == 'false' ]; then
                  $SCAN_PASSED=1
                fi
              else 
                TYPE=$(echo $RESULT | awk 'BEGIN{FS=","}{print $3}')
                for SETTING in $@; do
                  NAME=$(echo $SETTING | awk 'BEGIN{FS="="}{print $1}')
                  ACTION=$(echo $SETTING | awk 'BEGIN{FS="="}{print $2}')
                  SHOWN=1
                  # echo "$NAME=$TYPE"
                  # echo $ACTION
                  if [ $NAME == $TYPE ] && [ $ACTION == 'error' ]; then
                    RESULT=$(echo $RESULT | sed s/didYouMean/typosquat/g)
                    if [ -z $OUTPUT ]; then
                      OUTPUT=$RESULT
                    else
                      OUTPUT="$OUTPUT;$RESULT"
                    fi
                    if [ $SCAN_PASSED -eq 0 ]; then
                      SCAN_PASSED=1
                      # echo "SCAN FAILED"
                    fi
                  fi
                  if [ $SHOW_ALL -eq 1 ] && [ $SHOWN -eq 1 ]; then
                    if [ -z $OUTPUT ]; then
                      OUTPUT=$RESULT
                    else
                      OUTPUT="$OUTPUT;$RESULT"
                    fi
                  fi
                done
              fi
            done
    
            if [ $SCAN_PASSED -eq 0 ]; then
              echo "scan_passed=true"
            else
              echo "scan_passed=false"
              echo "Adding comment to merge request"
              comment_for_merge="# Socket Security: Security issues detected.\n\nLearn more about [socket.dev](https://socket.dev/?utm_medium=gh)\n\nThe following issues were detected\n\n| Severity | Category | Type | Ecosystem | Package |\\n| -------- | -------- | ---- | --------- | ------- |\\n|$(echo $OUTPUT | sed 's/;/|\\n|/g' | sed 's/,/|/g')|" 
              curl -s --location --request POST "https://gitlab.com/api/v4/projects/$CI_MERGE_REQUEST_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes" --header "PRIVATE-TOKEN: $GITLAB_TOKEN" --header "Content-Type: application/json" --data-raw "{ \"body\": \"$comment_for_merge\" }"
            fi
    
            echo $OUTPUT | sed 's/;/\n/g'
            exit $SCAN_PASSED
    
  4. Commit changes to your main branch or the current branch you are working on

Testing pipeline

  1. Create a new branch

  2. Modify or add a package.json

  3. Create a new Merge request

  4. Confirm that the Socket CI pipeline job ran

  5. Confirm that for an unhealthy report a comment is left on the Merge request


What’s Next

That's it! You're all done now any time there is an update to your manifest file the Socket CI will automatically run. You can update the criteria to add more things like requirements.txt or other lock files.