ci(secu): check dependency actions #136
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: dependency-checks | |
| on: | |
| pull_request: | |
| workflow_call: | |
| inputs: | |
| module_directory: | |
| description: 'Component path' | |
| required: false | |
| default: "" | |
| type: string | |
| permissions: | |
| contents: read | |
| env: | |
| minimum_package_age_hours: 1 # 168 # 7 days | |
| jobs: | |
| dependency-scan: | |
| name: Run safe-chain analysis | |
| runs-on: ${{ github.repository_visibility != 'public' && 'centreon-security' || 'ubuntu-24.04' }} | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Pnpm | |
| uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 | |
| with: | |
| version: 10 | |
| - name: Setup Node | |
| uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 | |
| with: | |
| node-version: 22 | |
| package-manager-cache: true | |
| - name: Install safe-chain | |
| run: | | |
| max_retries=5 | |
| retry_delay=5 | |
| for ((i=1; i<=max_retries; i++)); do | |
| if curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh -s -- --ci; then | |
| echo "Success on attempt $i" | |
| exit 0 | |
| else | |
| echo "Attempt $i failed. Retrying in $retry_delay seconds..." | |
| sleep $retry_delay | |
| fi | |
| done | |
| echo "All $max_retries attempts failed." | |
| exit 1 | |
| shell: bash | |
| - name: Check dependencies and managers | |
| run: | | |
| set +x | |
| # Check override | |
| if [ "${{ vars.OVERRIDE_ALL_DEPENDENCY_SCAN }}" == "true" ]; then | |
| echo "[DEBUG] - Global scan override enabled" | |
| return 0 | |
| elif [[ "${{ vars.OVERRIDE_PNPM_COMPLIANCE }}" == "true" ]]; then | |
| echo "[DEBUG] - PNPM scan override enabled" | |
| return 0 | |
| fi | |
| # Check date | |
| current_timestamp=$(date +%s) | |
| DUE_DATE="${{ vars.OVERRIDE_DEPENDENCY_ENFORCEMENT_DATE }}" | |
| input_timestamp=$(date -d "$DUE_DATE" +%s) | |
| # Setup vars | |
| export SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS=${{ env.minimum_package_age_hours }} | |
| export SAFE_CHAIN_LOGGING="silent" | |
| ERROR_LOG="error_log.txt" | |
| FORCE_FAIL="false" | |
| ENFORCEMENT="false" | |
| if [ "$current_timestamp" -ge "$input_timestamp" ]; then | |
| echo "[DEBUG]: Deadline passed." | |
| ENFORCEMENT="true" | |
| fi | |
| function message_type() { | |
| if [ $ENFORCEMENT == "true" ]; then | |
| echo "[ERROR] : $1" >> "$ERROR_LOG" | |
| FORCE_FAIL="true" | |
| else | |
| echo "[WARNING] : $1" >> "$ERROR_LOG" | |
| fi | |
| } | |
| # Compare pnpm version used in lockfile | |
| function compare_version() { | |
| SKIP="false" | |
| MIN_LOCKFILE_VERSION="9.0.0" | |
| LOCKFILE_VERSION=$(grep -E '^lockfileVersion:' "$LOC_TYPE" \ | |
| | awk '{print $2}' \ | |
| | tr -d "'\"") | |
| # Compare versions | |
| version_ge() { | |
| [ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" = "$2" ] | |
| } | |
| echo "[DEBUG] - 0 - before condition LOC-VERSION = $LOCKFILE_VERSION" | |
| if version_ge "$MIN_LOCKFILE_VERSION" "$LOCKFILE_VERSION"; then | |
| echo "[DEBUG] - 1 - LOCKFILE_VERSION is lower than $MIN_LOCKFILE_VERSION" | |
| echo "[DEBUG] - 1b - Analysis skipped" | |
| message_type "lockfileVersion $LOCKFILE_VERSION < $MIN_LOCKFILE_VERSION in $LOC_FILE" | |
| SKIP="true" | |
| fi | |
| } | |
| # Move to module folder and find manifests | |
| MODULE_FOLDER="${{ inputs.module_directory }}" || "" | |
| if [[ -n "$MODULE_FOLDER" ]]; then | |
| cd $MODULE_FOLDER | |
| fi | |
| touch "$ERROR_LOG" | |
| echo "[INFO] - Find manifest files" | |
| DEP_FILES=($(find ./ -type f -name "package.json")) | |
| # Scan node manifests | |
| for DEP_FILE in ${DEP_FILES[@]}; do | |
| DEP_DIR=$(dirname $DEP_FILE) | |
| echo "[INFO] - Scanning $DEP_FILE" | |
| echo "[DEBUG] - DEP_FILE = $DEP_FILE" | |
| LOC_FILES=($(find $DEP_DIR -type f -name "package-lock.json" -o -name "pnpm-lock.yaml" -o -name "yarn.lock")) | |
| COUNT=0 | |
| for LOC_FILE in ${LOC_FILES[@]}; do | |
| LOC_TYPE=$(basename $LOC_FILE) | |
| LOC_DIR=$(dirname $LOC_FILE) | |
| cd $LOC_DIR | |
| case $LOC_TYPE in | |
| "yarn.lock") | |
| COUNT=$((COUNT+1)) | |
| message_type "Yarn is no longer allowed. Kindly replace the lockfile using PNPM : $LOC_FILE" | |
| ;; | |
| "package-lock.json") | |
| COUNT=$((COUNT+1)) | |
| message_type "NPM is no longer allowed. Kindly replace the lockfile using PNPM : $LOC_FILE" | |
| ;; | |
| "pnpm-lock.yaml") | |
| echo -e "\n\n" | |
| echo "[DEBUG] - get pnpm version" | |
| pnpm -v | |
| echo "[DEBUG] - get node version" | |
| node -v | |
| echo "[DEBUG] - compare version start" | |
| local skip=$(compare_version) | |
| if [ "$SKIP" == "true" ] ; then continue; fi | |
| echo "[DEBUG] - check manifests" | |
| ls -la package.json | |
| ls -la pnpm-lock.yaml | |
| echo "[DEBUG] - LOC_FILE = $LOC_FILE" | |
| echo "[DEBUG] - safe chain start" | |
| #pnpm install --frozen-lockfile --loglevel warn --safe-chain-logging=verbose | |
| pnpm install --frozen-lockfile --safe-chain-logging=verbose | |
| COUNT=$((COUNT+1)) | |
| ;; | |
| "*") | |
| message_type "no lockfile found in $LOC_DIR" | |
| ;; | |
| esac | |
| cd - | |
| done | |
| if [[ $COUNT -gt 1 ]]; then | |
| message_type "$COUNT lockfiles were found. Kindly keep only the lockfile generated with PNPM : $LOC_DIR" | |
| fi | |
| echo "[DEBUG] - COUNT = $COUNT" | |
| done | |
| echo "[DEBUG] - FORCE_FAIL = $FORCE_FAIL" | |
| if [ -s "$ERROR_LOG" ]; then | |
| cat "$ERROR_LOG" | |
| if [ "FORCE_FAIL" == "true" ]; then | |
| echo "[FATAL]: Breaking the run." | |
| exit 1 | |
| fi | |
| else | |
| echo "[INFO] - OK nothing found" | |
| fi | |
| shell: bash | |
| dependency-blacklist: | |
| name: Run blacklist analysis | |
| runs-on: ${{ github.repository_visibility != 'public' && 'centreon-security' || 'ubuntu-24.04' }} | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for blacklisted dependencies | |
| run: | | |
| # Check override | |
| if [ "${{ vars.OVERRIDE_DEPENDENCY_SCAN }}" == "true" ]; then | |
| echo "[DEBUG] - Global scan override enabled" | |
| return 0 | |
| elif [[ "${{ vars.OVERRIDE_BLACKLIST_COMPLIANCE }}" == "true" ]]; then | |
| echo "[DEBUG] - Blacklist scan override enabled" | |
| return 0 | |
| fi | |
| function checkPnpmLockfile() { | |
| # Find dependency formated as | |
| # "name@version:" | |
| if grep -qF "$NAME@$VERSION" "$LOCKFILE"; then | |
| echo "$NAME:$VERSION was found in $LOCKFILE" | |
| echo "[ERROR] - $NAME:$VERSION was found in $LOCKFILE" >> "$ERROR_LOG" | |
| else | |
| echo -n "." | |
| fi | |
| } | |
| function checkManifest() { | |
| COUNT=0 | |
| echo "[INFO] - Testing manifest $LOCKFILE" | |
| manifest_type=$(basename "$LOCKFILE") | |
| while IFS=':' read -r NAME VERSION; do | |
| # ignore empty and commented lines | |
| [[ -z "${NAME// }" ]] && continue | |
| [[ "$NAME" =~ ^# ]] && continue | |
| case "$manifest_type" in | |
| "pnpm-lock.yaml") | |
| checkPnpmLockfile | |
| ;; | |
| "yarn.lock") | |
| echo "[ERROR] - dependency manager not allowed. Found in $LOCKFILE" >> "$ERROR_LOG" | |
| ;; | |
| "package-lock.json") | |
| echo "[ERROR] - dependency manager not allowed. Found in $LOCKFILE" >> "$ERROR_LOG" | |
| ;; | |
| "*") | |
| echo "[ERROR] - dependency manager not managed. Found in $LOCKFILE" >> "$ERROR_LOG" | |
| esac | |
| COUNT=$((COUNT+1)) | |
| done < "$DEP_LIST" | |
| echo "[INFO] - Scanned $COUNT IOC" | |
| } | |
| DEP_LIST="compromised-packages.txt" | |
| if [ -f "$DEP_LIST" ]; then rm -f "$DEP_LIST"; fi | |
| wget https://raw.githubusercontent.com/centreon/security-tools/main/blacklist/"$DEP_LIST" | |
| ERROR_LOG="error_log.txt" | |
| touch "$ERROR_LOG" | |
| LOCKFILES=($(find ./ -type f -name "pnpm-lock.yaml")) | |
| for LOCKFILE in "${LOCKFILES[@]}"; do | |
| checkManifest "$LOCKFILE" | |
| done | |
| if [ -s "$ERROR_LOG" ]; then | |
| echo -e "[ERROR]: Breaking the run as following ERRORS were found:" | |
| cat "$ERROR_LOG" | |
| exit 1 | |
| else | |
| echo "[INFO] - OK nothing found" | |
| fi | |
| shell: bash |