From de72bc39515b342d42615ebdf025359110263d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Mon, 13 Jan 2025 10:31:16 +0700 Subject: [PATCH 01/17] added env variable initializations --- .github/workflows/ensureSCCoreDevApproval.yml | 1 + .github/workflows/protectAuditLabels.yml | 2 ++ .github/workflows/protectSecurityRelevantCode.yml | 2 ++ .github/workflows/verifyAudit.yml | 11 ++++++----- .github/workflows/versionCheck.yml | 2 ++ .../ensureDeployProcessIntegrity.yml | 2 ++ .../workflows_deactivated/ensureSCCoreDevApproval.yml | 2 ++ .github/workflows_deactivated/protectAuditFolder.yml | 2 ++ 8 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ensureSCCoreDevApproval.yml b/.github/workflows/ensureSCCoreDevApproval.yml index 3651382e2..ce95e8148 100644 --- a/.github/workflows/ensureSCCoreDevApproval.yml +++ b/.github/workflows/ensureSCCoreDevApproval.yml @@ -17,6 +17,7 @@ jobs: env: GH_PAT: ${{ secrets.GIT_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CONTINUE: false # makes sure that variable is correctly initialized in all cases run: | ##### unset the default git token (does not have sufficient rights to get team members) diff --git a/.github/workflows/protectAuditLabels.yml b/.github/workflows/protectAuditLabels.yml index de2a3935d..b33faa182 100644 --- a/.github/workflows/protectAuditLabels.yml +++ b/.github/workflows/protectAuditLabels.yml @@ -15,6 +15,8 @@ on: jobs: protect_audit_labels: runs-on: ubuntu-latest + env: + CONTINUE: false # makes sure that variable is correctly initialized in all cases steps: - name: Checkout repository diff --git a/.github/workflows/protectSecurityRelevantCode.yml b/.github/workflows/protectSecurityRelevantCode.yml index 39046bdae..e35d331d9 100644 --- a/.github/workflows/protectSecurityRelevantCode.yml +++ b/.github/workflows/protectSecurityRelevantCode.yml @@ -13,6 +13,8 @@ jobs: protect-critical-code: if: ${{ github.event.pull_request.draft == false }} runs-on: ubuntu-latest + env: + CONTINUE: false # makes sure that variable is correctly initialized in all cases permissions: pull-requests: write steps: diff --git a/.github/workflows/verifyAudit.yml b/.github/workflows/verifyAudit.yml index d27463381..64f9617fa 100644 --- a/.github/workflows/verifyAudit.yml +++ b/.github/workflows/verifyAudit.yml @@ -25,6 +25,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} AUDIT_LOG_PATH: 'audit/auditLog.json' PR_NUMBER: ${{ github.event.pull_request.number }} + CONTINUE: false # makes sure that variable is correctly initialized in all cases permissions: pull-requests: write @@ -67,8 +68,8 @@ jobs: echo -e "\033[31mProtected contracts found in this PR.\033[0m" echo "PROTECTED_CONTRACTS: $PROTECTED_CONTRACTS" echo "AUDIT_REQUIRED=true" >> "$GITHUB_ENV" - echo "$AUDIT_REQUIRED" > audit_required.txt echo -e "$PROTECTED_CONTRACTS" > protected_contracts.txt + echo "CONTINUE=true" >> "$GITHUB_ENV" fi - name: Assign, update, and verify labels based on check outcome @@ -159,7 +160,7 @@ jobs: - name: Check Audit Log continue-on-error: true id: check-audit-log - if: env.AUDIT_REQUIRED == 'true' + if: always() && env.CONTINUE == 'true' # always() ensures that validation is always executed, even if env variable is not set run: | echo "This step will make sure that an audit is logged for each contract modified/added by this PR." @@ -381,7 +382,7 @@ jobs: echo "Assigning label 'AuditCompleted' next" - name: Assign label "AuditCompleted" if all checks passed - if: ${{ env.AUDIT_REQUIRED == 'true' && env.CONTINUE == 'true' }} + if: ${{ always() && env.AUDIT_REQUIRED == 'true' && env.CONTINUE == 'true' }} uses: actions-ecosystem/action-add-labels@v1 id: assign_label with: @@ -392,14 +393,14 @@ jobs: - name: Remove label "AuditCompleted" in case check was not successful but label was assigned in earlier checks continue-on-error: true # This ensures the step will execute even if the job has a failed status. uses: actions-ecosystem/action-remove-labels@v1 - if: ${{ env.AUDIT_COMPLETED_ASSIGNED && (env.CONTINUE == 'false' || (env.CONTINUE == 'true' && env.AUDIT_REQUIRED == 'false'))}} + if: ${{ always() && env.AUDIT_COMPLETED_ASSIGNED && (env.CONTINUE == 'false' || (env.CONTINUE == 'true' && env.AUDIT_REQUIRED == 'false'))}} with: github_token: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} # we use the token of the lifi-action-bot so the label protection check will pass labels: 'AuditCompleted' number: ${{ env.PR_NUMBER }} - name: Fail the git action if any critical step failed - if: env.CONTINUE == 'false' # This step runs only if a failure was recorded + if: always() && env.CONTINUE == 'false' # This step runs only if a failure was recorded run: | echo -e "\033[31mError: One or more critical steps failed. Failing the job.\033[0m" diff --git a/.github/workflows/versionCheck.yml b/.github/workflows/versionCheck.yml index a7c42f3f5..7245cfac4 100644 --- a/.github/workflows/versionCheck.yml +++ b/.github/workflows/versionCheck.yml @@ -18,6 +18,8 @@ jobs: # will only run once the PR is in "Ready for Review" state if: ${{ github.event.pull_request.draft == false }} runs-on: ubuntu-latest + env: + CONTINUE: false # makes sure that variable is correctly initialized in all cases steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows_deactivated/ensureDeployProcessIntegrity.yml b/.github/workflows_deactivated/ensureDeployProcessIntegrity.yml index 6ae3bb93a..5e2d18374 100644 --- a/.github/workflows_deactivated/ensureDeployProcessIntegrity.yml +++ b/.github/workflows_deactivated/ensureDeployProcessIntegrity.yml @@ -11,6 +11,8 @@ on: jobs: protect-security-system: runs-on: ubuntu-latest + env: + CONTINUE: false # makes sure that variable is correctly initialized in all cases permissions: pull-requests: write steps: diff --git a/.github/workflows_deactivated/ensureSCCoreDevApproval.yml b/.github/workflows_deactivated/ensureSCCoreDevApproval.yml index db633e505..0902a7bd3 100644 --- a/.github/workflows_deactivated/ensureSCCoreDevApproval.yml +++ b/.github/workflows_deactivated/ensureSCCoreDevApproval.yml @@ -14,6 +14,8 @@ jobs: core-dev-approval: if: ${{ github.event.pull_request.draft == false }} # will only run once the PR is in "Ready for Review" state runs-on: ubuntu-latest + env: + CONTINUE: false # makes sure that variable is correctly initialized in all cases steps: - name: Get smart-contract-core Team Members env: diff --git a/.github/workflows_deactivated/protectAuditFolder.yml b/.github/workflows_deactivated/protectAuditFolder.yml index d97390a00..c5860db6b 100644 --- a/.github/workflows_deactivated/protectAuditFolder.yml +++ b/.github/workflows_deactivated/protectAuditFolder.yml @@ -15,6 +15,8 @@ on: jobs: protect-audit-folder: runs-on: ubuntu-latest + env: + CONTINUE: false # makes sure that variable is correctly initialized in all cases steps: - name: Checkout code From da5273f9b91dba975d05a7698a854dfff0f0393c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Wed, 15 Jan 2025 06:18:46 +0700 Subject: [PATCH 02/17] temp - DO NOT PUSH --- .github/workflows/verifyAudit.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/verifyAudit.yml b/.github/workflows/verifyAudit.yml index 64f9617fa..0705195f0 100644 --- a/.github/workflows/verifyAudit.yml +++ b/.github/workflows/verifyAudit.yml @@ -47,6 +47,7 @@ jobs: if [[ -z $FILES ]]; then echo -e "\033[31mNo files found. This should not happen. Please check the code of the Github action. Aborting now.\033[0m" echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 fi ##### Initialize empty variables @@ -69,9 +70,11 @@ jobs: echo "PROTECTED_CONTRACTS: $PROTECTED_CONTRACTS" echo "AUDIT_REQUIRED=true" >> "$GITHUB_ENV" echo -e "$PROTECTED_CONTRACTS" > protected_contracts.txt - echo "CONTINUE=true" >> "$GITHUB_ENV" fi + ##### Set continue flag to true to make sure subsequent steps will run + echo "CONTINUE=true" >> "$GITHUB_ENV" + - name: Assign, update, and verify labels based on check outcome uses: actions/github-script@v7 env: From 810b52b1b3d95b11d4ff7bdab04175f4fe10f836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 09:31:00 +0700 Subject: [PATCH 03/17] test --- .github/workflows/verifyAudit_OLD.yml | 406 ++++++++++++++++++++++++++ audit/auditLog.json | 2 +- 2 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/verifyAudit_OLD.yml diff --git a/.github/workflows/verifyAudit_OLD.yml b/.github/workflows/verifyAudit_OLD.yml new file mode 100644 index 000000000..368cff3f2 --- /dev/null +++ b/.github/workflows/verifyAudit_OLD.yml @@ -0,0 +1,406 @@ +name: Audit Verifier +# - checks if an audit is required and assigns a (protected) label based on the result ('AuditRequired' or 'AuditNotRequired') +# - if an audit is required, it will verify that the audit was actually completed and then assign label "AuditCompleted" +# - verification includes: +# - ensuring the audit log contains an entry for all added/modified contracts in their latest version +# - ensuring that an audit report has been added +# - ensuring that the PR is approved by the auditor (uses auditor git handle from audit log) +# - ensuring that the commit hash that was audited is actually part of this PR +# - if "AuditCompleted" was assigned earlier but conditions are not met anymore due to changes submitted after the check, label "AuditCompleted" will be removed by this action + +# KNOWN LIMITATIONS +# - will only check the last 100 commits for any matches with audit commit hashes +# - only one audit can be registered per contract (in a specific version) + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +jobs: + verify-audit_OLD: + # will only run once the PR is in "Ready for Review" state + if: ${{ github.event.pull_request.draft == false }} + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} + AUDIT_LOG_PATH: 'audit/auditLog.json' + PR_NUMBER: ${{ github.event.pull_request.number }} + + permissions: + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 ##### Fetch all history for all branches + + - name: Check PR for changes in protected folders ('src/*') + id: check_git_diff_for_protectected_folders + run: | + + + ##### Get all files modified by this PR + FILES=$(git diff --name-only origin/main HEAD) + + ##### Make sure that there are modified files + if [[ -z $FILES ]]; then + echo -e "\033[31mNo files found. This should not happen. Please check the code of the Github action. Aborting now.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + fi + + ##### Initialize empty variables + PROTECTED_CONTRACTS="" + + ##### Go through all modified file names/paths and identify contracts with path 'src/*' + while IFS= read -r FILE; do + if echo "$FILE" | grep -E '^src/.*\.sol$'; then + ##### Contract found + PROTECTED_CONTRACTS="${PROTECTED_CONTRACTS}${FILE}"$'\n' + fi + done <<< "$FILES" + + ##### Determine if audit is required + if [[ -z "$PROTECTED_CONTRACTS" ]]; then + echo -e "\033[32mNo protected contracts found in this PR.\033[0m" + echo "AUDIT_REQUIRED=false" >> "$GITHUB_ENV" + else + echo -e "\033[31mProtected contracts found in this PR.\033[0m" + echo "PROTECTED_CONTRACTS: $PROTECTED_CONTRACTS" + echo "AUDIT_REQUIRED=true" >> "$GITHUB_ENV" + echo "$AUDIT_REQUIRED" > audit_required.txt + echo -e "$PROTECTED_CONTRACTS" > protected_contracts.txt + fi + + - name: Assign, update, and verify labels based on check outcome + uses: actions/github-script@v7 + env: + AUDIT_REQUIRED: ${{ env.AUDIT_REQUIRED }} + with: + script: | + const { execSync } = require('child_process'); + + // ANSI escape codes for colors (used for colored output in Git action console) + const colors = { + reset: "\033[0m", + red: "\033[31m", + green: "\033[32m", + }; + + // Fetch currently assigned labels from GitHub using GitHub CLI + let assignedLabels = []; + try { + // Fetch the labels directly from the pull request + const labelsOutput = execSync(`gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name'`).toString(); + + // Split the labels output into an array and trim each label + assignedLabels = labelsOutput.split('\n').map(label => label.trim()).filter(Boolean); + } catch (error) { + console.error(`${colors.red}Error fetching assigned labels: ${error.message}${colors.reset}`); + process.exit(1); + } + + // check if audit is required (determined by previous step) + const auditRequired = process.env.AUDIT_REQUIRED === 'true'; + + // determine which label should be assigned and which should be removed + const labelToAssign = auditRequired ? 'AuditRequired' : 'AuditNotRequired'; + const oppositeLabel = auditRequired ? 'AuditNotRequired' : 'AuditRequired'; + + console.log(`Currently assigned labels: ${JSON.stringify(assignedLabels)}`); + console.log(`Label '${labelToAssign}' has to be assigned to this PR`); + console.log(`Label '${oppositeLabel}' will be removed, if currently present`); + + // Assign the required label if not already present + if (!assignedLabels.includes(labelToAssign)) { + console.log(`Now assigning label: ${labelToAssign}`); + execSync(`gh pr edit ${{ github.event.pull_request.number }} --add-label "${labelToAssign}"`, { stdio: 'inherit' }); + } else { + console.log(`${colors.green}Label "${labelToAssign}" is already assigned. No action needed.${colors.reset}`); + } + + // Remove the opposite label if it is present + if (assignedLabels.includes(oppositeLabel)) { + console.log(`Now removing opposite label: ${oppositeLabel}`); + execSync(`gh pr edit ${{ github.event.pull_request.number }} --remove-label "${oppositeLabel}"`, { stdio: 'inherit' }); + } else { + console.log(`${colors.green}Opposite label "${oppositeLabel}" is not assigned. No action needed.${colors.reset}`); + } + + // fetch all currently assigned labels again + assignedLabels = [] + try { + // Fetch the labels directly from the pull request + const labelsOutput = execSync(`gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name'`).toString(); + + // Split the labels output into an array and trim each label + assignedLabels = labelsOutput.split('\n').map(label => label.trim()).filter(Boolean); + } catch (error) { + console.error(`${colors.red}Error fetching assigned labels: ${error.message}${colors.reset}`); + process.exit(1); + } + + // Verify that exactly one of the two labels is assigned + const totalLabelsAssigned = assignedLabels.filter(label => ['AuditRequired', 'AuditNotRequired'].includes(label)).length; + + if (totalLabelsAssigned !== 1) { + console.error(`${colors.red}Error: Exactly one of the two protected labels should be assigned but found ${totalLabelsAssigned} assigned labels.${colors.reset}`); + process.exit(1); + } else { + console.log(`${colors.green}Verified that exactly one label is assigned. Check passed :)${colors.reset}`); + } + + if(assignedLabels.includes('AuditCompleted')) { + console.log(`Label 'AuditCompleted' is currently assigned`); + core.exportVariable('AUDIT_COMPLETED_ASSIGNED', 'true'); + } + else + console.log(`Label 'AuditCompleted' is currently not assigned`); + + - name: Check Audit Log + continue-on-error: true + id: check-audit-log + if: env.AUDIT_REQUIRED == 'true' + run: | + + echo "This step will make sure that an audit is logged for each contract modified/added by this PR." + echo "It will also make sure that no information is missing in the audit log and that the information is meaningful." + + # load list of protected contracts + PROTECTED_CONTRACTS=$(cat protected_contracts.txt) + + # create temp files to store commit hashes and auditor handles + COMMIT_HASHES_FILE="commit_hashes.txt" + AUDITOR_GIT_HANDLES_FILE="auditor_handles.txt" + + ##### make sure that there are any protected contracts + if [[ -z $PROTECTED_CONTRACTS ]]; then + echo -e "\033[31mNo protected contracts found. This should not happen (action should stop earlier). Please check the code of the Github action. Aborting now.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 + fi + + # iterate through all contracts + while IFS= read -r FILE; do + echo "-----------" + echo "now checking file $FILE" + ##### load contract version + VERSION=$(sed -nE 's/^\/\/\/ @custom:version ([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' "$FILE") + + ##### make sure that contract version was extracted successfully + if [[ -z $VERSION ]]; then + echo -e "\033[31mCould not find version of contract $FILE. This should not happen. Please check the Github action code. Aborting now.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 + fi + + ##### see if audit log contains an entry with those values + FILENAME=$(basename "$FILE" .sol) + + ##### Check if the contract and version exist in the JSON and get the audit IDs + AUDIT_IDS=$(jq -r --arg filename "$FILENAME" --arg version "$VERSION" \ + 'if .auditedContracts[$filename][$version] != null then .auditedContracts[$filename][$version][] else empty end' "$AUDIT_LOG_PATH") + + ##### Count the number of audits found in the log for this contract/version + if [[ -z "$AUDIT_IDS" ]]; then + AUDIT_COUNT=0 + else + AUDIT_COUNT=$(echo "$AUDIT_IDS" | wc -l) + fi + + ##### Ensure exactly one audit is logged; handle errors if not + if [[ $AUDIT_COUNT -ne 1 ]]; then + echo "CONTINUE=false" >> "$GITHUB_ENV" + + if [[ $AUDIT_COUNT -gt 1 ]]; then + echo -e "\033[31mError: Multiple audits found for contract $FILENAME in version $VERSION.\033[0m" + echo -e "\033[31mOnly one audit should be logged per contract version.\033[0m" + echo -e "\033[31mPlease fix the audit log and try again.\033[0m" + else + echo -e "\033[31mError: Could not find a logged audit for contract $FILENAME in version $VERSION.\033[0m" + echo -e "\033[31mThis check will not pass until the audit log contains a completed audit for this file.\033[0m" + fi + + exit 1 + fi + + + ##### Extract the single audit ID + AUDIT_ID=$(echo "$AUDIT_IDS" | head -n 1) + + ##### Extract audit entry details for the single audit ID + AUDIT_ENTRY=$(jq -r --arg audit_id "$AUDIT_ID" '.audits[$audit_id]' "$AUDIT_LOG_PATH") + + ##### Check if AUDIT_ENTRY is valid JSON + if [[ -z "$AUDIT_ENTRY" || "$AUDIT_ENTRY" == "null" ]]; then + echo -e "\033[31mError: The logged audit ID ($AUDIT_ID) for contract $FILE seems to be invalid.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 + fi + + echo "File $FILE was audited in $AUDIT_ID" + echo "Now checking if all required information is logged for this audit..." + + ##### Extract log entry values into variables + AUDIT_COMPLETED_ON=$(echo "$AUDIT_ENTRY" | jq -r '.auditCompletedOn') + AUDITED_BY=$(echo "$AUDIT_ENTRY" | jq -r '.auditedBy') + AUDITOR_GIT_HANDLE=$(echo "$AUDIT_ENTRY" | jq -r '.auditorGitHandle') + AUDIT_REPORT_PATH=$(echo "$AUDIT_ENTRY" | jq -r '.auditReportPath') + AUDIT_COMMIT_HASH=$(echo "$AUDIT_ENTRY" | jq -r '.auditCommitHash') + + ##### make sure that audit log entry contains date + if [ -z "$AUDIT_COMPLETED_ON" ]; then + echo -e "\033[31mThe audit log entry for file $FILE contains an invalid or no 'auditCompletedOn' date.\033[0m" + echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" + echo -e "\033[31mAborting now.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 + else + echo "The audit log contains a date for $AUDIT_ID: $AUDIT_COMPLETED_ON" + fi + + ##### make sure that audit log entry contains auditor's (company) name + if [ -z "$AUDITED_BY" ]; then + echo -e "\033[31mThe audit log entry for file $FILE contains invalid or no 'auditedBy' information.\033[0m" + echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" + echo -e "\033[31mAborting now.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 + else + echo "The audit log contains the auditor's name for $AUDIT_ID: $AUDITED_BY" + fi + + ##### make sure that audit log entry contains auditor's git handle + if [ -z "$AUDITOR_GIT_HANDLE" ]; then + echo -e "\033[31mThe audit log entry for file $FILE contains invalid or no 'auditorGitHandle' information.\033[0m" + echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" + echo -e "\033[31mAborting now.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 + else + echo "The audit log contains the auditor's github handle for $AUDIT_ID: $AUDITOR_GIT_HANDLE" + fi + + ##### make sure that a file exists at the audit report path + if [ ! -f "$AUDIT_REPORT_PATH" ]; then + echo -e "\033[31mCould not find an audit report in path $AUDIT_REPORT_PATH for contract "$FILENAME".\033[0m" + echo -e "\033[31mThis github action cannot complete before the audit report is uploaded to 'audit/reports/'.\033[0m" + echo -e "\033[31mAborting now.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 + else + echo "The audit report for $AUDIT_ID was found in path $AUDIT_REPORT_PATH" + fi + + ##### make sure that audit log entry contains audit commit hash + if [ -z "$AUDIT_COMMIT_HASH" ]; then + echo -e "\033[31mThe audit log entry for file $FILE contains invalid or no 'auditCommitHash' information.\033[0m" + echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" + echo -e "\033[31mAborting now.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 + else + echo "The audit log contains the commit hash that was audited in $AUDIT_ID: $AUDIT_COMMIT_HASH" + fi + echo -e "\033[32mThe audit log contains all required information for contract $FILE\033[0m" + + echo "now checking if audit commit hash $AUDIT_COMMIT_HASH is associated with PR $PR_NUMBER" + ##### Fetch the list of commits associated with the PR + COMMIT_LIST=$(gh pr view "$PR_NUMBER" --json commits --jq '.commits[].oid') + + ##### Check if the target commit is in the list + if echo "$COMMIT_LIST" | grep -q "$TARGET_COMMIT"; then + echo -e "\033[32mCommit $AUDIT_COMMIT_HASH is associated with PR #$PR_NUMBER.\033[0m" + else + echo -e "\033[31mCommit $AUDIT_COMMIT_HASH is NOT associated with PR #$PR_NUMBER.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 + fi + + + ##### Check if the auditor git handle exists on github + echo "now checking if the auditor git handle ($AUDITOR_GIT_HANDLE) actually exists" + if gh api users/$AUDITOR_GIT_HANDLE > /dev/null 2>&1; then + echo -e "\033[32mA user with handle '$AUDITOR_GIT_HANDLE' exists on GitHub.\033[0m" + else + echo -e "\033[31mA user with handle '$AUDITOR_GIT_HANDLE' does not exist on GitHub.\033[0m" + echo -e "\033[31mPlease fix the audit log before continuing.\033[0m" + echo -e "\033[31mCheck failed.\033[0m" + echo "CONTINUE=false" >> "$GITHUB_ENV" + exit 1 + fi + + # ##### ----------------------------------------------------------------------------- + # ##### DISABLED FOR NOW (NEED TO CHECK IF THIS IS COMPATIBLE WITH OUR FLOW) + # ##### Fetch PR reviews using the GitHub API via gh cli + # echo "now checking if the auditor ($AUDITOR_GIT_HANDLE) approved this PR ($PR_NUMBER)" + # REVIEWS=$(gh api repos/lifinance/contracts/pulls/$PR_NUMBER/reviews --jq '.[] | select(.state == "APPROVED") | @json') + + # ##### Check if the output is empty or not valid JSON + # if [[ -z "$REVIEWS" ]]; then + # echo "ERROR: No reviews found or failed to fetch reviews for PR #$PR_NUMBER" + # exit 1 + # fi + # ##### Flag to track if the review by the specified person is found + # FOUND_REVIEW=false + + # ##### Check if the desired reviewer is present among the reviews + # echo "$REVIEWS" | jq -c '.' | while read -r REVIEW; do + # AUTHOR=$(echo "$REVIEW" | jq -r '.user.login // empty') + # STATE=$(echo "$REVIEW" | jq -r '.state // empty') + + # echo "found review by $AUTHOR with state $STATE" + + + # ##### Check if the reviewer is the person we're looking for + # if [ "$AUTHOR" == "$REVIEWER" ]; then + # echo "Approving review found by $REVIEWER" + # FOUND_REVIEW=true + # exit 0 + # fi + # done + + # ##### If no matching review was found, exit with an error + # if [ "$FOUND_REVIEW" == true ]; then + # echo -e "\033[32mPR $PR_NUMBER has an approving review by $AUDITOR_GIT_HANDLE\033[0m" + # echo -e "\033[32mCheck passed\033[0m" + # exit 0 + # else + # echo -e "\033[31mERROR: No review found by git user '$AUDITOR_GIT_HANDLE' (= the auditor)\033[0m" + # echo -e "\033[31mCheck failed\033[0m" + # echo "CONTINUE=false" >> "$GITHUB_ENV" + # exit 1 + # fi + # ##### ----------------------------------------------------------------------------- + + + done <<< "$PROTECTED_CONTRACTS" + + echo -e "\033[32mSuccessfully verified that all contracts in this PRs are audited.\033[0m" + echo -e "\033[32mCheck passed.\033[0m" + echo "CONTINUE=true" >> "$GITHUB_ENV" + echo "Assigning label 'AuditCompleted' next" + + - name: Assign label "AuditCompleted" if all checks passed + if: ${{ env.AUDIT_REQUIRED == 'true' && env.CONTINUE == 'true' }} + uses: actions-ecosystem/action-add-labels@v1 + id: assign_label + with: + github_token: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} # we use the token of the lifi-action-bot so the label protection check will pass + labels: 'AuditCompleted' + number: ${{ env.PR_NUMBER }} + + - name: Remove label "AuditCompleted" in case check was not successful but label was assigned in earlier checks + continue-on-error: true # This ensures the step will execute even if the job has a failed status. + uses: actions-ecosystem/action-remove-labels@v1 + if: ${{ env.AUDIT_COMPLETED_ASSIGNED && (env.CONTINUE == 'false' || (env.CONTINUE == 'true' && env.AUDIT_REQUIRED == 'false'))}} + with: + github_token: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} # we use the token of the lifi-action-bot so the label protection check will pass + labels: 'AuditCompleted' + number: ${{ env.PR_NUMBER }} + + - name: Fail the git action if any critical step failed + if: env.CONTINUE == 'false' # This step runs only if a failure was recorded + run: | + + echo -e "\033[31mError: One or more critical steps failed. Failing the job.\033[0m" + exit 1 diff --git a/audit/auditLog.json b/audit/auditLog.json index 50e1354a0..23fe5b30f 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -69,7 +69,7 @@ "auditorGitHandle": "sujithsomraaj", "auditReportPath": "./audit/reports/2024.12.03_LiFiDexAggregator.pdf", "auditCommitHash": "8a34562c912b5b19c919bb95338655c944428af5" - }, + } "audit20241205": { "auditCompletedOn": "05.12.2024", "auditedBy": "Sujith Somraaj (individual security researcher)", From fce56dcc87bb8569cfd1352daaa3717b40f4d8d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 09:33:42 +0700 Subject: [PATCH 04/17] test --- src/Facets/AcrossFacet.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Facets/AcrossFacet.sol b/src/Facets/AcrossFacet.sol index 3cb75ca7f..4736a7cb5 100644 --- a/src/Facets/AcrossFacet.sol +++ b/src/Facets/AcrossFacet.sol @@ -13,7 +13,7 @@ import { Validatable } from "../Helpers/Validatable.sol"; /// @title Across Facet /// @author LI.FI (https://li.fi) /// @notice Provides functionality for bridging through Across Protocol -/// @custom:version 2.0.0 +/// @custom:version 2.1.0 contract AcrossFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// Storage /// @@ -46,6 +46,8 @@ contract AcrossFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { wrappedNative = _wrappedNative; } + function bla() external {} + /// External Methods /// /// @notice Bridges tokens via Across From efa6549d31e9215d064153b0d123961205a34857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 09:43:54 +0700 Subject: [PATCH 05/17] test --- .github/workflows/verifyAudit.yml | 8 ++++---- .github/workflows/verifyAudit_OLD.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/verifyAudit.yml b/.github/workflows/verifyAudit.yml index 0705195f0..085f593c7 100644 --- a/.github/workflows/verifyAudit.yml +++ b/.github/workflows/verifyAudit.yml @@ -46,7 +46,7 @@ jobs: ##### Make sure that there are modified files if [[ -z $FILES ]]; then echo -e "\033[31mNo files found. This should not happen. Please check the code of the Github action. Aborting now.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" + echo "CONTINUE=false" >> "$GITHUB_ENV" # explicit assignment, even though already initialized with false exit 1 fi @@ -72,9 +72,6 @@ jobs: echo -e "$PROTECTED_CONTRACTS" > protected_contracts.txt fi - ##### Set continue flag to true to make sure subsequent steps will run - echo "CONTINUE=true" >> "$GITHUB_ENV" - - name: Assign, update, and verify labels based on check outcome uses: actions/github-script@v7 env: @@ -160,6 +157,9 @@ jobs: else console.log(`Label 'AuditCompleted' is currently not assigned`); + // set CONTINUE to true to make sure the following steps are executed + core.exportVariable('CONTINUE', 'true'); + - name: Check Audit Log continue-on-error: true id: check-audit-log diff --git a/.github/workflows/verifyAudit_OLD.yml b/.github/workflows/verifyAudit_OLD.yml index 368cff3f2..5518c6593 100644 --- a/.github/workflows/verifyAudit_OLD.yml +++ b/.github/workflows/verifyAudit_OLD.yml @@ -19,7 +19,7 @@ on: jobs: verify-audit_OLD: # will only run once the PR is in "Ready for Review" state - if: ${{ github.event.pull_request.draft == false }} + if: ${{ github.event.pull_request.draft == true }} runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} From 29f6105fec667a87567b84b7a078f55ddffba748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 09:47:38 +0700 Subject: [PATCH 06/17] test --- .github/workflows/verifyAudit.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/verifyAudit.yml b/.github/workflows/verifyAudit.yml index 085f593c7..63b89b997 100644 --- a/.github/workflows/verifyAudit.yml +++ b/.github/workflows/verifyAudit.yml @@ -127,6 +127,12 @@ jobs: console.log(`${colors.green}Opposite label "${oppositeLabel}" is not assigned. No action needed.${colors.reset}`); } + // Remove the AuditCompleted label if it is present + if (assignedLabels.includes('AuditCompleted')) { + console.log(`Now removing AuditCompleted label`); + execSync(`gh pr edit ${{ github.event.pull_request.number }} --remove-label "AuditCompleted"`, { stdio: 'inherit' }); + } + // fetch all currently assigned labels again assignedLabels = [] try { From 8429fe34a732fa567aa3ecbe9f2558e7d1ef2d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 09:49:56 +0700 Subject: [PATCH 07/17] test --- .github/workflows/verifyAudit.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/verifyAudit.yml b/.github/workflows/verifyAudit.yml index 63b89b997..28fe8cb80 100644 --- a/.github/workflows/verifyAudit.yml +++ b/.github/workflows/verifyAudit.yml @@ -80,6 +80,7 @@ jobs: script: | const { execSync } = require('child_process'); + console.log(`CONTINUE=${CONTINUE}`) // ANSI escape codes for colors (used for colored output in Git action console) const colors = { reset: "\033[0m", @@ -172,6 +173,9 @@ jobs: if: always() && env.CONTINUE == 'true' # always() ensures that validation is always executed, even if env variable is not set run: | + echo "CONTINUE=$CONTINUE" + + echo "This step will make sure that an audit is logged for each contract modified/added by this PR." echo "It will also make sure that no information is missing in the audit log and that the information is meaningful." From bcac001e9a2712b7db548c9a3d3743beab805977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 09:52:18 +0700 Subject: [PATCH 08/17] test --- .github/workflows/verifyAudit.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/verifyAudit.yml b/.github/workflows/verifyAudit.yml index 28fe8cb80..5baadcb9c 100644 --- a/.github/workflows/verifyAudit.yml +++ b/.github/workflows/verifyAudit.yml @@ -76,11 +76,12 @@ jobs: uses: actions/github-script@v7 env: AUDIT_REQUIRED: ${{ env.AUDIT_REQUIRED }} + CONTINUE: ${{ env.CONTINUE }} with: script: | const { execSync } = require('child_process'); - console.log(`CONTINUE=${CONTINUE}`) + console.log(`CONTINUE=${core.read}`) // ANSI escape codes for colors (used for colored output in Git action console) const colors = { reset: "\033[0m", From 29563133fe68a61d063e73da64e622a64dbfd387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 09:57:52 +0700 Subject: [PATCH 09/17] test --- .github/workflows/verifyAudit.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/verifyAudit.yml b/.github/workflows/verifyAudit.yml index 5baadcb9c..58d7135c1 100644 --- a/.github/workflows/verifyAudit.yml +++ b/.github/workflows/verifyAudit.yml @@ -175,6 +175,9 @@ jobs: run: | echo "CONTINUE=$CONTINUE" + echo "CONTINUE=false" >> "$GITHUB_ENV" + echo "CONTINUE=$CONTINUE" + echo "This step will make sure that an audit is logged for each contract modified/added by this PR." From 7203408e0e092e34d9cf696c3e6c424deb92d79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 10:02:07 +0700 Subject: [PATCH 10/17] test --- .github/workflows/verifyAudit.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/verifyAudit.yml b/.github/workflows/verifyAudit.yml index 58d7135c1..44ee9bd25 100644 --- a/.github/workflows/verifyAudit.yml +++ b/.github/workflows/verifyAudit.yml @@ -163,7 +163,7 @@ jobs: core.exportVariable('AUDIT_COMPLETED_ASSIGNED', 'true'); } else - console.log(`Label 'AuditCompleted' is currently not assigned`); + console.log(`${colors.green}Label 'AuditCompleted' is currently not assigned${colors.reset}`); // set CONTINUE to true to make sure the following steps are executed core.exportVariable('CONTINUE', 'true'); @@ -174,15 +174,12 @@ jobs: if: always() && env.CONTINUE == 'true' # always() ensures that validation is always executed, even if env variable is not set run: | - echo "CONTINUE=$CONTINUE" - echo "CONTINUE=false" >> "$GITHUB_ENV" - echo "CONTINUE=$CONTINUE" - - - echo "This step will make sure that an audit is logged for each contract modified/added by this PR." echo "It will also make sure that no information is missing in the audit log and that the information is meaningful." + # set CONTINUE to false to ensure that action fails (correctly) if an error occurs unexpectedly + echo "CONTINUE=false" >> "$GITHUB_ENV" + # load list of protected contracts PROTECTED_CONTRACTS=$(cat protected_contracts.txt) From 9e36cd2f2033ec4e195b673d421cdd64f4c63ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 10:02:41 +0700 Subject: [PATCH 11/17] test --- .github/workflows/verifyAudit.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/verifyAudit.yml b/.github/workflows/verifyAudit.yml index 44ee9bd25..840dc909f 100644 --- a/.github/workflows/verifyAudit.yml +++ b/.github/workflows/verifyAudit.yml @@ -81,7 +81,6 @@ jobs: script: | const { execSync } = require('child_process'); - console.log(`CONTINUE=${core.read}`) // ANSI escape codes for colors (used for colored output in Git action console) const colors = { reset: "\033[0m", From 9ed80d72ae0f2b5cf5c6015f5908e44a66d66087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 10:03:58 +0700 Subject: [PATCH 12/17] revert changes in contract --- src/Facets/AcrossFacet.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Facets/AcrossFacet.sol b/src/Facets/AcrossFacet.sol index 4736a7cb5..3cb75ca7f 100644 --- a/src/Facets/AcrossFacet.sol +++ b/src/Facets/AcrossFacet.sol @@ -13,7 +13,7 @@ import { Validatable } from "../Helpers/Validatable.sol"; /// @title Across Facet /// @author LI.FI (https://li.fi) /// @notice Provides functionality for bridging through Across Protocol -/// @custom:version 2.1.0 +/// @custom:version 2.0.0 contract AcrossFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// Storage /// @@ -46,8 +46,6 @@ contract AcrossFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { wrappedNative = _wrappedNative; } - function bla() external {} - /// External Methods /// /// @notice Bridges tokens via Across From 3684bfff471ea4a70e439d4346b6b2bafc13eefc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 10:04:26 +0700 Subject: [PATCH 13/17] remove old verifier actin code --- .github/workflows/verifyAudit_OLD.yml | 406 -------------------------- 1 file changed, 406 deletions(-) delete mode 100644 .github/workflows/verifyAudit_OLD.yml diff --git a/.github/workflows/verifyAudit_OLD.yml b/.github/workflows/verifyAudit_OLD.yml deleted file mode 100644 index 5518c6593..000000000 --- a/.github/workflows/verifyAudit_OLD.yml +++ /dev/null @@ -1,406 +0,0 @@ -name: Audit Verifier -# - checks if an audit is required and assigns a (protected) label based on the result ('AuditRequired' or 'AuditNotRequired') -# - if an audit is required, it will verify that the audit was actually completed and then assign label "AuditCompleted" -# - verification includes: -# - ensuring the audit log contains an entry for all added/modified contracts in their latest version -# - ensuring that an audit report has been added -# - ensuring that the PR is approved by the auditor (uses auditor git handle from audit log) -# - ensuring that the commit hash that was audited is actually part of this PR -# - if "AuditCompleted" was assigned earlier but conditions are not met anymore due to changes submitted after the check, label "AuditCompleted" will be removed by this action - -# KNOWN LIMITATIONS -# - will only check the last 100 commits for any matches with audit commit hashes -# - only one audit can be registered per contract (in a specific version) - -on: - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - -jobs: - verify-audit_OLD: - # will only run once the PR is in "Ready for Review" state - if: ${{ github.event.pull_request.draft == true }} - runs-on: ubuntu-latest - env: - GITHUB_TOKEN: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} - AUDIT_LOG_PATH: 'audit/auditLog.json' - PR_NUMBER: ${{ github.event.pull_request.number }} - - permissions: - pull-requests: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 ##### Fetch all history for all branches - - - name: Check PR for changes in protected folders ('src/*') - id: check_git_diff_for_protectected_folders - run: | - - - ##### Get all files modified by this PR - FILES=$(git diff --name-only origin/main HEAD) - - ##### Make sure that there are modified files - if [[ -z $FILES ]]; then - echo -e "\033[31mNo files found. This should not happen. Please check the code of the Github action. Aborting now.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - fi - - ##### Initialize empty variables - PROTECTED_CONTRACTS="" - - ##### Go through all modified file names/paths and identify contracts with path 'src/*' - while IFS= read -r FILE; do - if echo "$FILE" | grep -E '^src/.*\.sol$'; then - ##### Contract found - PROTECTED_CONTRACTS="${PROTECTED_CONTRACTS}${FILE}"$'\n' - fi - done <<< "$FILES" - - ##### Determine if audit is required - if [[ -z "$PROTECTED_CONTRACTS" ]]; then - echo -e "\033[32mNo protected contracts found in this PR.\033[0m" - echo "AUDIT_REQUIRED=false" >> "$GITHUB_ENV" - else - echo -e "\033[31mProtected contracts found in this PR.\033[0m" - echo "PROTECTED_CONTRACTS: $PROTECTED_CONTRACTS" - echo "AUDIT_REQUIRED=true" >> "$GITHUB_ENV" - echo "$AUDIT_REQUIRED" > audit_required.txt - echo -e "$PROTECTED_CONTRACTS" > protected_contracts.txt - fi - - - name: Assign, update, and verify labels based on check outcome - uses: actions/github-script@v7 - env: - AUDIT_REQUIRED: ${{ env.AUDIT_REQUIRED }} - with: - script: | - const { execSync } = require('child_process'); - - // ANSI escape codes for colors (used for colored output in Git action console) - const colors = { - reset: "\033[0m", - red: "\033[31m", - green: "\033[32m", - }; - - // Fetch currently assigned labels from GitHub using GitHub CLI - let assignedLabels = []; - try { - // Fetch the labels directly from the pull request - const labelsOutput = execSync(`gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name'`).toString(); - - // Split the labels output into an array and trim each label - assignedLabels = labelsOutput.split('\n').map(label => label.trim()).filter(Boolean); - } catch (error) { - console.error(`${colors.red}Error fetching assigned labels: ${error.message}${colors.reset}`); - process.exit(1); - } - - // check if audit is required (determined by previous step) - const auditRequired = process.env.AUDIT_REQUIRED === 'true'; - - // determine which label should be assigned and which should be removed - const labelToAssign = auditRequired ? 'AuditRequired' : 'AuditNotRequired'; - const oppositeLabel = auditRequired ? 'AuditNotRequired' : 'AuditRequired'; - - console.log(`Currently assigned labels: ${JSON.stringify(assignedLabels)}`); - console.log(`Label '${labelToAssign}' has to be assigned to this PR`); - console.log(`Label '${oppositeLabel}' will be removed, if currently present`); - - // Assign the required label if not already present - if (!assignedLabels.includes(labelToAssign)) { - console.log(`Now assigning label: ${labelToAssign}`); - execSync(`gh pr edit ${{ github.event.pull_request.number }} --add-label "${labelToAssign}"`, { stdio: 'inherit' }); - } else { - console.log(`${colors.green}Label "${labelToAssign}" is already assigned. No action needed.${colors.reset}`); - } - - // Remove the opposite label if it is present - if (assignedLabels.includes(oppositeLabel)) { - console.log(`Now removing opposite label: ${oppositeLabel}`); - execSync(`gh pr edit ${{ github.event.pull_request.number }} --remove-label "${oppositeLabel}"`, { stdio: 'inherit' }); - } else { - console.log(`${colors.green}Opposite label "${oppositeLabel}" is not assigned. No action needed.${colors.reset}`); - } - - // fetch all currently assigned labels again - assignedLabels = [] - try { - // Fetch the labels directly from the pull request - const labelsOutput = execSync(`gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name'`).toString(); - - // Split the labels output into an array and trim each label - assignedLabels = labelsOutput.split('\n').map(label => label.trim()).filter(Boolean); - } catch (error) { - console.error(`${colors.red}Error fetching assigned labels: ${error.message}${colors.reset}`); - process.exit(1); - } - - // Verify that exactly one of the two labels is assigned - const totalLabelsAssigned = assignedLabels.filter(label => ['AuditRequired', 'AuditNotRequired'].includes(label)).length; - - if (totalLabelsAssigned !== 1) { - console.error(`${colors.red}Error: Exactly one of the two protected labels should be assigned but found ${totalLabelsAssigned} assigned labels.${colors.reset}`); - process.exit(1); - } else { - console.log(`${colors.green}Verified that exactly one label is assigned. Check passed :)${colors.reset}`); - } - - if(assignedLabels.includes('AuditCompleted')) { - console.log(`Label 'AuditCompleted' is currently assigned`); - core.exportVariable('AUDIT_COMPLETED_ASSIGNED', 'true'); - } - else - console.log(`Label 'AuditCompleted' is currently not assigned`); - - - name: Check Audit Log - continue-on-error: true - id: check-audit-log - if: env.AUDIT_REQUIRED == 'true' - run: | - - echo "This step will make sure that an audit is logged for each contract modified/added by this PR." - echo "It will also make sure that no information is missing in the audit log and that the information is meaningful." - - # load list of protected contracts - PROTECTED_CONTRACTS=$(cat protected_contracts.txt) - - # create temp files to store commit hashes and auditor handles - COMMIT_HASHES_FILE="commit_hashes.txt" - AUDITOR_GIT_HANDLES_FILE="auditor_handles.txt" - - ##### make sure that there are any protected contracts - if [[ -z $PROTECTED_CONTRACTS ]]; then - echo -e "\033[31mNo protected contracts found. This should not happen (action should stop earlier). Please check the code of the Github action. Aborting now.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - fi - - # iterate through all contracts - while IFS= read -r FILE; do - echo "-----------" - echo "now checking file $FILE" - ##### load contract version - VERSION=$(sed -nE 's/^\/\/\/ @custom:version ([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' "$FILE") - - ##### make sure that contract version was extracted successfully - if [[ -z $VERSION ]]; then - echo -e "\033[31mCould not find version of contract $FILE. This should not happen. Please check the Github action code. Aborting now.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - fi - - ##### see if audit log contains an entry with those values - FILENAME=$(basename "$FILE" .sol) - - ##### Check if the contract and version exist in the JSON and get the audit IDs - AUDIT_IDS=$(jq -r --arg filename "$FILENAME" --arg version "$VERSION" \ - 'if .auditedContracts[$filename][$version] != null then .auditedContracts[$filename][$version][] else empty end' "$AUDIT_LOG_PATH") - - ##### Count the number of audits found in the log for this contract/version - if [[ -z "$AUDIT_IDS" ]]; then - AUDIT_COUNT=0 - else - AUDIT_COUNT=$(echo "$AUDIT_IDS" | wc -l) - fi - - ##### Ensure exactly one audit is logged; handle errors if not - if [[ $AUDIT_COUNT -ne 1 ]]; then - echo "CONTINUE=false" >> "$GITHUB_ENV" - - if [[ $AUDIT_COUNT -gt 1 ]]; then - echo -e "\033[31mError: Multiple audits found for contract $FILENAME in version $VERSION.\033[0m" - echo -e "\033[31mOnly one audit should be logged per contract version.\033[0m" - echo -e "\033[31mPlease fix the audit log and try again.\033[0m" - else - echo -e "\033[31mError: Could not find a logged audit for contract $FILENAME in version $VERSION.\033[0m" - echo -e "\033[31mThis check will not pass until the audit log contains a completed audit for this file.\033[0m" - fi - - exit 1 - fi - - - ##### Extract the single audit ID - AUDIT_ID=$(echo "$AUDIT_IDS" | head -n 1) - - ##### Extract audit entry details for the single audit ID - AUDIT_ENTRY=$(jq -r --arg audit_id "$AUDIT_ID" '.audits[$audit_id]' "$AUDIT_LOG_PATH") - - ##### Check if AUDIT_ENTRY is valid JSON - if [[ -z "$AUDIT_ENTRY" || "$AUDIT_ENTRY" == "null" ]]; then - echo -e "\033[31mError: The logged audit ID ($AUDIT_ID) for contract $FILE seems to be invalid.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - fi - - echo "File $FILE was audited in $AUDIT_ID" - echo "Now checking if all required information is logged for this audit..." - - ##### Extract log entry values into variables - AUDIT_COMPLETED_ON=$(echo "$AUDIT_ENTRY" | jq -r '.auditCompletedOn') - AUDITED_BY=$(echo "$AUDIT_ENTRY" | jq -r '.auditedBy') - AUDITOR_GIT_HANDLE=$(echo "$AUDIT_ENTRY" | jq -r '.auditorGitHandle') - AUDIT_REPORT_PATH=$(echo "$AUDIT_ENTRY" | jq -r '.auditReportPath') - AUDIT_COMMIT_HASH=$(echo "$AUDIT_ENTRY" | jq -r '.auditCommitHash') - - ##### make sure that audit log entry contains date - if [ -z "$AUDIT_COMPLETED_ON" ]; then - echo -e "\033[31mThe audit log entry for file $FILE contains an invalid or no 'auditCompletedOn' date.\033[0m" - echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" - echo -e "\033[31mAborting now.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - else - echo "The audit log contains a date for $AUDIT_ID: $AUDIT_COMPLETED_ON" - fi - - ##### make sure that audit log entry contains auditor's (company) name - if [ -z "$AUDITED_BY" ]; then - echo -e "\033[31mThe audit log entry for file $FILE contains invalid or no 'auditedBy' information.\033[0m" - echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" - echo -e "\033[31mAborting now.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - else - echo "The audit log contains the auditor's name for $AUDIT_ID: $AUDITED_BY" - fi - - ##### make sure that audit log entry contains auditor's git handle - if [ -z "$AUDITOR_GIT_HANDLE" ]; then - echo -e "\033[31mThe audit log entry for file $FILE contains invalid or no 'auditorGitHandle' information.\033[0m" - echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" - echo -e "\033[31mAborting now.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - else - echo "The audit log contains the auditor's github handle for $AUDIT_ID: $AUDITOR_GIT_HANDLE" - fi - - ##### make sure that a file exists at the audit report path - if [ ! -f "$AUDIT_REPORT_PATH" ]; then - echo -e "\033[31mCould not find an audit report in path $AUDIT_REPORT_PATH for contract "$FILENAME".\033[0m" - echo -e "\033[31mThis github action cannot complete before the audit report is uploaded to 'audit/reports/'.\033[0m" - echo -e "\033[31mAborting now.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - else - echo "The audit report for $AUDIT_ID was found in path $AUDIT_REPORT_PATH" - fi - - ##### make sure that audit log entry contains audit commit hash - if [ -z "$AUDIT_COMMIT_HASH" ]; then - echo -e "\033[31mThe audit log entry for file $FILE contains invalid or no 'auditCommitHash' information.\033[0m" - echo -e "\033[31mThis github action cannot complete before the audit log is complete.\033[0m" - echo -e "\033[31mAborting now.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - else - echo "The audit log contains the commit hash that was audited in $AUDIT_ID: $AUDIT_COMMIT_HASH" - fi - echo -e "\033[32mThe audit log contains all required information for contract $FILE\033[0m" - - echo "now checking if audit commit hash $AUDIT_COMMIT_HASH is associated with PR $PR_NUMBER" - ##### Fetch the list of commits associated with the PR - COMMIT_LIST=$(gh pr view "$PR_NUMBER" --json commits --jq '.commits[].oid') - - ##### Check if the target commit is in the list - if echo "$COMMIT_LIST" | grep -q "$TARGET_COMMIT"; then - echo -e "\033[32mCommit $AUDIT_COMMIT_HASH is associated with PR #$PR_NUMBER.\033[0m" - else - echo -e "\033[31mCommit $AUDIT_COMMIT_HASH is NOT associated with PR #$PR_NUMBER.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - fi - - - ##### Check if the auditor git handle exists on github - echo "now checking if the auditor git handle ($AUDITOR_GIT_HANDLE) actually exists" - if gh api users/$AUDITOR_GIT_HANDLE > /dev/null 2>&1; then - echo -e "\033[32mA user with handle '$AUDITOR_GIT_HANDLE' exists on GitHub.\033[0m" - else - echo -e "\033[31mA user with handle '$AUDITOR_GIT_HANDLE' does not exist on GitHub.\033[0m" - echo -e "\033[31mPlease fix the audit log before continuing.\033[0m" - echo -e "\033[31mCheck failed.\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - fi - - # ##### ----------------------------------------------------------------------------- - # ##### DISABLED FOR NOW (NEED TO CHECK IF THIS IS COMPATIBLE WITH OUR FLOW) - # ##### Fetch PR reviews using the GitHub API via gh cli - # echo "now checking if the auditor ($AUDITOR_GIT_HANDLE) approved this PR ($PR_NUMBER)" - # REVIEWS=$(gh api repos/lifinance/contracts/pulls/$PR_NUMBER/reviews --jq '.[] | select(.state == "APPROVED") | @json') - - # ##### Check if the output is empty or not valid JSON - # if [[ -z "$REVIEWS" ]]; then - # echo "ERROR: No reviews found or failed to fetch reviews for PR #$PR_NUMBER" - # exit 1 - # fi - # ##### Flag to track if the review by the specified person is found - # FOUND_REVIEW=false - - # ##### Check if the desired reviewer is present among the reviews - # echo "$REVIEWS" | jq -c '.' | while read -r REVIEW; do - # AUTHOR=$(echo "$REVIEW" | jq -r '.user.login // empty') - # STATE=$(echo "$REVIEW" | jq -r '.state // empty') - - # echo "found review by $AUTHOR with state $STATE" - - - # ##### Check if the reviewer is the person we're looking for - # if [ "$AUTHOR" == "$REVIEWER" ]; then - # echo "Approving review found by $REVIEWER" - # FOUND_REVIEW=true - # exit 0 - # fi - # done - - # ##### If no matching review was found, exit with an error - # if [ "$FOUND_REVIEW" == true ]; then - # echo -e "\033[32mPR $PR_NUMBER has an approving review by $AUDITOR_GIT_HANDLE\033[0m" - # echo -e "\033[32mCheck passed\033[0m" - # exit 0 - # else - # echo -e "\033[31mERROR: No review found by git user '$AUDITOR_GIT_HANDLE' (= the auditor)\033[0m" - # echo -e "\033[31mCheck failed\033[0m" - # echo "CONTINUE=false" >> "$GITHUB_ENV" - # exit 1 - # fi - # ##### ----------------------------------------------------------------------------- - - - done <<< "$PROTECTED_CONTRACTS" - - echo -e "\033[32mSuccessfully verified that all contracts in this PRs are audited.\033[0m" - echo -e "\033[32mCheck passed.\033[0m" - echo "CONTINUE=true" >> "$GITHUB_ENV" - echo "Assigning label 'AuditCompleted' next" - - - name: Assign label "AuditCompleted" if all checks passed - if: ${{ env.AUDIT_REQUIRED == 'true' && env.CONTINUE == 'true' }} - uses: actions-ecosystem/action-add-labels@v1 - id: assign_label - with: - github_token: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} # we use the token of the lifi-action-bot so the label protection check will pass - labels: 'AuditCompleted' - number: ${{ env.PR_NUMBER }} - - - name: Remove label "AuditCompleted" in case check was not successful but label was assigned in earlier checks - continue-on-error: true # This ensures the step will execute even if the job has a failed status. - uses: actions-ecosystem/action-remove-labels@v1 - if: ${{ env.AUDIT_COMPLETED_ASSIGNED && (env.CONTINUE == 'false' || (env.CONTINUE == 'true' && env.AUDIT_REQUIRED == 'false'))}} - with: - github_token: ${{ secrets.GIT_ACTIONS_BOT_PAT_CLASSIC }} # we use the token of the lifi-action-bot so the label protection check will pass - labels: 'AuditCompleted' - number: ${{ env.PR_NUMBER }} - - - name: Fail the git action if any critical step failed - if: env.CONTINUE == 'false' # This step runs only if a failure was recorded - run: | - - echo -e "\033[31mError: One or more critical steps failed. Failing the job.\033[0m" - exit 1 From 3cbae5d8a8a1e92a767d39ea1f79b2f4903f21dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 10:10:10 +0700 Subject: [PATCH 14/17] test --- .github/workflows/verifyAudit.yml | 2 +- audit/auditLog.json | 178 ++++++++---------------------- 2 files changed, 46 insertions(+), 134 deletions(-) diff --git a/.github/workflows/verifyAudit.yml b/.github/workflows/verifyAudit.yml index 840dc909f..96afc40e3 100644 --- a/.github/workflows/verifyAudit.yml +++ b/.github/workflows/verifyAudit.yml @@ -170,7 +170,7 @@ jobs: - name: Check Audit Log continue-on-error: true id: check-audit-log - if: always() && env.CONTINUE == 'true' # always() ensures that validation is always executed, even if env variable is not set + if: ${{ always() && env.AUDIT_REQUIRED == 'true' && env.CONTINUE == 'true' }} # always() ensures that validation is always executed, even if env variable is not set run: | echo "This step will make sure that an audit is logged for each contract modified/added by this PR." diff --git a/audit/auditLog.json b/audit/auditLog.json index 23fe5b30f..f9f3ed599 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -69,7 +69,7 @@ "auditorGitHandle": "sujithsomraaj", "auditReportPath": "./audit/reports/2024.12.03_LiFiDexAggregator.pdf", "auditCommitHash": "8a34562c912b5b19c919bb95338655c944428af5" - } + }, "audit20241205": { "auditCompletedOn": "05.12.2024", "auditedBy": "Sujith Somraaj (individual security researcher)", @@ -129,188 +129,100 @@ }, "auditedContracts": { "AcrossFacetV3": { - "1.0.0": [ - "audit20241007" - ], - "1.1.0": [ - "audit20250106" - ] + "1.0.0": ["audit20241007"], + "1.1.0": ["audit20250106"] }, "AcrossFacetPackedV3": { - "1.0.0": [ - "audit20241007" - ], - "1.2.0": [ - "audit20241206" - ] + "1.0.0": ["audit20241007"], + "1.2.0": ["audit20241206"] }, "CalldataVerificationFacet": { - "1.2.0": [ - "audit20240902" - ] + "1.2.0": ["audit20240902"] }, "DeBridgeDlnFacet": { - "1.0.0": [ - "audit20241205" - ] + "1.0.0": ["audit20241205"] }, "EmergencyPauseFacet": { - "1.0.0": [ - "audit20240913" - ], - "1.0.1": [ - "audit20241105" - ] + "1.0.0": ["audit20240913"], + "1.0.1": ["audit20241105"] }, "ERC20Proxy": { - "1.1.0": [ - "audit20250109_2" - ] + "1.1.0": ["audit20250109_2"] }, "Executor": { - "2.1.0": [ - "audit20250109_2" - ] + "2.1.0": ["audit20250109_2"] }, "FeeCollector": { - "1.0.1": [ - "audit20250109_3" - ] + "1.0.1": ["audit20250109_3"] }, "GasZipFacet": { - "2.0.0": [ - "audit20241107" - ], - "2.0.2": [ - "audit20250110_1" - ] + "2.0.0": ["audit20241107"], + "2.0.2": ["audit20250110_1"] }, "GasZipPeriphery": { - "1.0.0": [ - "audit20241107" - ], - "1.0.1": [ - "audit20250110_1" - ] + "1.0.0": ["audit20241107"], + "1.0.1": ["audit20250110_1"] }, "IAcrossSpokePool": { - "1.0.0": [ - "audit20250106" - ] + "1.0.0": ["audit20250106"] }, "IGasZip": { - "1.0.0": [ - "audit20241107" - ] + "1.0.0": ["audit20241107"] }, "LibAsset": { - "1.0.1": [ - "audit20241202" - ], - "1.0.2": [ - "audit20250110_1" - ] + "1.0.1": ["audit20241202"], + "1.0.2": ["audit20250110_1"] }, "LiFiDEXAggregator": { - "1.5.0": [ - "audit20241203" - ], - "1.6.0": [ - "audit20250109_2" - ], - "1.5.1": [ - "audit20250109_3" - ] + "1.5.0": ["audit20241203"], + "1.6.0": ["audit20250109_2"], + "1.5.1": ["audit20250109_3"] }, "LiFiTimelockController": { - "1.0.0": [ - "audit20250110_2" - ] + "1.0.0": ["audit20250110_2"] }, "LiFuelFeeCollector": { - "1.0.2": [ - "audit20250109_3" - ] + "1.0.2": ["audit20250109_3"] }, "Permit2Proxy": { - "1.0.0": [ - "audit20241122" - ], - "1.0.1": [ - "audit20250110_1" - ], - "1.0.2": [ - "audit20250109_3" - ] + "1.0.0": ["audit20241122"], + "1.0.1": ["audit20250110_1"], + "1.0.2": ["audit20250109_3"] }, "Receiver": { - "2.0.3": [ - "audit20250109_3" - ], - "2.1.0": [ - "audit20250109_2" - ] + "2.0.3": ["audit20250109_3"], + "2.1.0": ["audit20250109_2"] }, "ReceiverAcrossV3": { - "1.0.0": [ - "audit20241007" - ], - "1.0.1": [ - "audit20241206" - ], - "1.0.2": [ - "audit20250110_1" - ], - "1.1.0": [ - "audit20250109_2" - ] + "1.0.0": ["audit20241007"], + "1.0.1": ["audit20241206"], + "1.0.2": ["audit20250110_1"], + "1.1.0": ["audit20250109_2"] }, "ReceiverStargateV2": { - "1.0.1": [ - "audit20250109_3" - ], - "1.1.0": [ - "audit20250109_2" - ] + "1.0.1": ["audit20250109_3"], + "1.1.0": ["audit20250109_2"] }, "RelayerCelerIM": { - "1.0.3": [ - "audit20250109_3" - ], - "2.1.0": [ - "audit20250109_2" - ], - "2.1.1": [ - "audit20250109_3" - ] + "1.0.3": ["audit20250109_3"], + "2.1.0": ["audit20250109_2"], + "2.1.1": ["audit20250109_3"] }, "RelayFacet": { - "1.0.0": [ - "audit20241202" - ] + "1.0.0": ["audit20241202"] }, "StargateFacetV2": { - "1.0.1": [ - "audit20240814" - ] + "1.0.1": ["audit20240814"] }, "ThorSwapFacet": { - "1.2.1": [ - "audit20250109_1" - ] + "1.2.1": ["audit20250109_1"] }, "TokenWrapper": { - "1.0.1": [ - "audit20250109_3" - ], - "1.1.0": [ - "audit20250109_2" - ] + "1.0.1": ["audit20250109_3"], + "1.1.0": ["audit20250109_2"] }, "WithdrawablePeriphery": { - "1.0.0": [ - "audit20241014" - ] + "1.0.0": ["audit20241014"] } } } From 11be5604a1f31d6e7916d1371c6fc48be29f4317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 10:19:18 +0700 Subject: [PATCH 15/17] test --- audit/auditLog.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audit/auditLog.json b/audit/auditLog.json index f9f3ed599..efed43665 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -69,7 +69,7 @@ "auditorGitHandle": "sujithsomraaj", "auditReportPath": "./audit/reports/2024.12.03_LiFiDexAggregator.pdf", "auditCommitHash": "8a34562c912b5b19c919bb95338655c944428af5" - }, + } "audit20241205": { "auditCompletedOn": "05.12.2024", "auditedBy": "Sujith Somraaj (individual security researcher)", From b22dec0ae6098876e24d7bd4fe8300ca0913dff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 10:20:29 +0700 Subject: [PATCH 16/17] test --- src/Facets/AcrossFacet.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Facets/AcrossFacet.sol b/src/Facets/AcrossFacet.sol index 3cb75ca7f..4736a7cb5 100644 --- a/src/Facets/AcrossFacet.sol +++ b/src/Facets/AcrossFacet.sol @@ -13,7 +13,7 @@ import { Validatable } from "../Helpers/Validatable.sol"; /// @title Across Facet /// @author LI.FI (https://li.fi) /// @notice Provides functionality for bridging through Across Protocol -/// @custom:version 2.0.0 +/// @custom:version 2.1.0 contract AcrossFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// Storage /// @@ -46,6 +46,8 @@ contract AcrossFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { wrappedNative = _wrappedNative; } + function bla() external {} + /// External Methods /// /// @notice Bridges tokens via Across From 792656949e4bd3836b992a6d034188c8e28088f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Bl=C3=A4cker?= Date: Thu, 16 Jan 2025 10:32:02 +0700 Subject: [PATCH 17/17] remove unused workflow files --- .github/workflows/protectAuditLabels.yml | 1 - .../enforceTestCoverage.yml | 141 -------------- .../ensureSCCoreDevApproval.yml | 123 ------------ .../protectAuditCompletedLabel.yml | 78 -------- audit/auditLog.json | 178 +++++++++++++----- src/Facets/AcrossFacet.sol | 4 +- 6 files changed, 134 insertions(+), 391 deletions(-) delete mode 100644 .github/workflows_deactivated/enforceTestCoverage.yml delete mode 100644 .github/workflows_deactivated/ensureSCCoreDevApproval.yml delete mode 100644 .github/workflows_deactivated/protectAuditCompletedLabel.yml diff --git a/.github/workflows/protectAuditLabels.yml b/.github/workflows/protectAuditLabels.yml index b33faa182..d7e78d3ff 100644 --- a/.github/workflows/protectAuditLabels.yml +++ b/.github/workflows/protectAuditLabels.yml @@ -48,7 +48,6 @@ jobs: echo "CONTINUE=false" >> $GITHUB_ENV exit 0 fi - echo "CONTINUE=true" >> $GITHUB_ENV echo "This action was triggered by: ${{ github.actor }}" - name: Protect Audit Labels diff --git a/.github/workflows_deactivated/enforceTestCoverage.yml b/.github/workflows_deactivated/enforceTestCoverage.yml deleted file mode 100644 index 1fff0d78e..000000000 --- a/.github/workflows_deactivated/enforceTestCoverage.yml +++ /dev/null @@ -1,141 +0,0 @@ -name: Enforce Min Test Coverage - -# - will make sure that (Foundry) unit test coverage is above min threshold -# - we start with 75%, planning to increase to 100% until EOY 2024 -# - Only the 'lines' coverage counts as 'branch' coverage is not reliable - -on: - pull_request: - types: [opened, synchronize, reopened] - -jobs: - enforce-min-test-coverage: - runs-on: ubuntu-latest - # will only run once the PR is in "Ready for Review" state - if: ${{ github.event.pull_request.draft == false }} - - permissions: - pull-requests: write - contents: read - env: - ETH_NODE_URI_MAINNET: ${{ secrets.ETH_NODE_URI_MAINNET }} - ETH_NODE_URI_POLYGON: ${{ secrets.ETH_NODE_URI_POLYGON }} - ETH_NODE_URI_GOERLI: ${{ secrets.ETH_NODE_URI_GOERLI }} - ETH_NODE_URI_ARBITRUM: ${{ secrets.ETH_NODE_URI_ARBITRUM }} - ETH_NODE_URI_BSC: ${{ secrets.ETH_NODE_URI_BSC }} - ETH_NODE_URI_GNOSIS: ${{ secrets.ETH_NODE_URI_GNOSIS }} - GIT_TOKEN: ${{ secrets.GIT_TOKEN }} - MIN_TEST_COVERAGE: 75 # 75 percent for now, will be increased to 100% gradually until the end of 2024 - steps: - - uses: actions/checkout@v4.1.7 - - - name: Set up Node.js - uses: actions/setup-node@v4.1.0 - with: - node-version: '20' - - - name: Install dev dependencies - run: yarn install - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1.2.0 - with: - version: nightly - - - name: Install Dependencies - run: forge install - - - name: Install Git Submodules - run: | - git config --global url."https://github.com/".insteadOf "git@github.com:" - git submodule update --init --recursive - - - name: Generate Coverage Report - run: | - forge coverage --report lcov --force - echo "Filtering coverage report to only contain coverage info about src/ folder now" - npx ts-node script/utils/filter_lcov.ts lcov.info lcov-filtered.info 'test/' 'script/' - echo "Coverage report filtered" - - - name: Generate Coverage Summary - run: | - # Path to the lcov info file - LCOV_FILE="lcov-filtered.info" - - # Initialize counters - TOTAL_LINES_FOUND=0 - TOTAL_LINES_HIT=0 - TOTAL_FUNCTIONS_FOUND=0 - TOTAL_FUNCTIONS_HIT=0 - TOTAL_BRANCHES_FOUND=0 - TOTAL_BRANCHES_HIT=0 - - # Read through the lcov file - while IFS= read -r line; do - case $line in - LF:*) - TOTAL_LINES_FOUND=$((TOTAL_LINES_FOUND + ${line#LF:})) - ;; - LH:*) - TOTAL_LINES_HIT=$((TOTAL_LINES_HIT + ${line#LH:})) - ;; - FNF:*) - TOTAL_FUNCTIONS_FOUND=$((TOTAL_FUNCTIONS_FOUND + ${line#FNF:})) - ;; - FNH:*) - TOTAL_FUNCTIONS_HIT=$((TOTAL_FUNCTIONS_HIT + ${line#FNH:})) - ;; - BRF:*) - TOTAL_BRANCHES_FOUND=$((TOTAL_BRANCHES_FOUND + ${line#BRF:})) - ;; - BRH:*) - TOTAL_BRANCHES_HIT=$((TOTAL_BRANCHES_HIT + ${line#BRH:})) - ;; - esac - done < "$LCOV_FILE" - - # Calculate percentages with high precision - LINE_COVERAGE_PERCENTAGE=$(echo "scale=4; $TOTAL_LINES_HIT / $TOTAL_LINES_FOUND * 100" | bc) - FUNCTION_COVERAGE_PERCENTAGE=$(echo "scale=4; $TOTAL_FUNCTIONS_HIT / $TOTAL_FUNCTIONS_FOUND * 100" | bc) - BRANCH_COVERAGE_PERCENTAGE=$(echo "scale=4; $TOTAL_BRANCHES_HIT / $TOTAL_BRANCHES_FOUND * 100" | bc) - - # Format results with two decimal places and alignment - LINE_COVERAGE_PERCENTAGE=$(printf "%.2f" "$LINE_COVERAGE_PERCENTAGE") - FUNCTION_COVERAGE_PERCENTAGE=$(printf "%.2f" "$FUNCTION_COVERAGE_PERCENTAGE") - BRANCH_COVERAGE_PERCENTAGE=$(printf "%.2f" "$BRANCH_COVERAGE_PERCENTAGE") - - # Prepare aligned output - LINE_COVERAGE_REPORT=$(printf "Line Coverage: %6s%% (%4d / %4d lines)" "$LINE_COVERAGE_PERCENTAGE" "$TOTAL_LINES_HIT" "$TOTAL_LINES_FOUND") - FUNCTION_COVERAGE_REPORT=$(printf "Function Coverage: %6s%% (%4d / %4d functions)" "$FUNCTION_COVERAGE_PERCENTAGE" "$TOTAL_FUNCTIONS_HIT" "$TOTAL_FUNCTIONS_FOUND") - BRANCH_COVERAGE_REPORT=$(printf "Branch Coverage: %6s%% (%4d / %4d branches)" "$BRANCH_COVERAGE_PERCENTAGE" "$TOTAL_BRANCHES_HIT" "$TOTAL_BRANCHES_FOUND") - - # Check against minimum threshold - if (( $(echo "$LINE_COVERAGE_PERCENTAGE >= $MIN_TEST_COVERAGE" | bc -l) )); then - RESULT_COVERAGE_REPORT="Test coverage ($LINE_COVERAGE_PERCENTAGE%) is above min threshold ($MIN_TEST_COVERAGE%). Check passed." - else - RESULT_COVERAGE_REPORT="Test coverage ($LINE_COVERAGE_PERCENTAGE%) is below min threshold ($MIN_TEST_COVERAGE%). Check failed." - echo $RESULT_COVERAGE_REPORT - exit 1 - fi - - # Output result_COVERAGE_REPORTs - echo "$LINE_COVERAGE_REPORT" - echo "$FUNCTION_COVERAGE_REPORT" - echo "$BRANCH_COVERAGE_REPORT" - echo "$RESULT_COVERAGE_REPORT" - - # Store in GitHub environment variables - echo "LINE_COVERAGE_REPORT=$LINE_COVERAGE_REPORT" >> $GITHUB_ENV - echo "FUNCTION_COVERAGE_REPORT=$FUNCTION_COVERAGE_REPORT" >> $GITHUB_ENV - echo "BRANCH_COVERAGE_REPORT=$BRANCH_COVERAGE_REPORT" >> $GITHUB_ENV - echo "RESULT_COVERAGE_REPORT=$RESULT_COVERAGE_REPORT" >> $GITHUB_ENV - - - name: Comment with Coverage Summary in PR - uses: mshick/add-pr-comment@v2 - with: - message: | - ## Test Coverage Report - ${{ env.LINE_COVERAGE_REPORT }} - ${{ env.FUNCTION_COVERAGE_REPORT }} - ${{ env.BRANCH_COVERAGE_REPORT }} - ${{ env.RESULT_COVERAGE_REPORT }} diff --git a/.github/workflows_deactivated/ensureSCCoreDevApproval.yml b/.github/workflows_deactivated/ensureSCCoreDevApproval.yml deleted file mode 100644 index 0902a7bd3..000000000 --- a/.github/workflows_deactivated/ensureSCCoreDevApproval.yml +++ /dev/null @@ -1,123 +0,0 @@ -# - Smart Contract Core Dev Approval checker -# - makes sure that every pull_request is at least reviewed by one Smart Contract Core Dev -# (member of group https://github.com/orgs/lifinance/teams/smart-contract-core) - -name: SC Core Dev Approval Check - -on: - pull_request: - types: [opened, synchronize, reopened] - pull_request_review: - types: [submitted] - -jobs: - core-dev-approval: - if: ${{ github.event.pull_request.draft == false }} # will only run once the PR is in "Ready for Review" state - runs-on: ubuntu-latest - env: - CONTINUE: false # makes sure that variable is correctly initialized in all cases - steps: - - name: Get smart-contract-core Team Members - env: - GH_PAT: ${{ secrets.GIT_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - - ##### unset the default git token (does not have sufficient rights to get team members) - unset GITHUB_TOKEN - ##### use the Personal Access Token to log into git CLI - echo $GH_PAT | gh auth login --with-token - - ##### Function that uses github's REST API via CLI to get team members - getTeamMembers() { - local org=$1 - local team=$2 - gh api \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "/orgs/$org/teams/$team/members" | jq -r '.[].login' - } - - ORG_NAME="lifinance" - TEAM_SLUG="smart-contract-core" - - # Get members of each group - echo "Fetching members of $TEAM_SLUG..." - MEMBERS=$(getTeamMembers $ORG_NAME $TEAM_SLUG) - - #### check if any members were returned - if [[ -z $MEMBERS ]]; then - echo -e "\033[31mERROR: Could not retrieve team members of group $TEAM_SLUG\033[0m" - echo "CONTINUE=false" >> "$GITHUB_ENV" - exit 1 - fi - - echo "The following Github users are members of team smart-contract-core: " - echo "$MEMBERS" - - echo -e "$MEMBERS" > sc_core_dev_members.txt - echo "CONTINUE=true" >> "$GITHUB_ENV" - - - name: Check if PR is approved by at least one SC core dev - id: check-core-dev-approval - if: env.CONTINUE == 'true' - uses: actions/github-script@v7 - env: - PR_NUMBER: ${{ github.event.number }} - with: - script: | - const fs = require('fs'); - // ANSI escape codes for colors (used for colored output in Git action console) - const colors = { - reset: "\033[0m", - red: "\033[31m", - green: "\033[32m", - }; - - const coreDevsFile = 'sc_core_dev_members.txt'; - // Read handles from file - const coreDevs = fs.readFileSync(coreDevsFile, 'utf-8').split(/\r?\n/).filter(Boolean); - - // get all reviewers that have approved this PR - const { data: reviews } = await github.rest.pulls.listReviews({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: process.env.PR_NUMBER, - }); - - // make sure that reviews are available - if(!reviews || reviews.length === 0) { - console.log(`${colors.red}Could not get reviewers of this PR from Github. Are there any reviews yet?${colors.reset}`); - console.log(`${colors.red}Check failed.${colors.reset}`); - core.setFailed("Required approval is missing"); - return - } - - // Filter to only include reviews that have "APPROVED" status - const approvedReviews = reviews.filter(review => review.state === 'APPROVED'); - - if(!approvedReviews.length === 0) { - console.log(`${colors.red}Could not find any reviews with approval.${colors.reset}`); - console.log(`${colors.red}Cannot continue. Check failed.${colors.reset}`); - core.setFailed("Required approval is missing"); - return - } - - // extract the git login handles of all reviewers that approved this PR - const reviewerHandles = approvedReviews.map(review => review.user.login); - - if(approvedReviews.length === 0) - console.log(`${colors.red}This PR has no approvals${colors.reset}`); - else - console.log(`This PR has been approved by the following git members: ${reviewerHandles}`); - - // check if at least one of these reviewers is member in smart-contract-core group - if (reviewerHandles.some((handle) => coreDevs.includes(handle))) { - console.log(`${colors.green}The current PR is approved by a member of the smart-contract-core group.${colors.reset}`); - console.log(`${colors.green}Check passed.${colors.reset}`); - core.setOutput('approved', 'true'); - } else { - console.log(`${colors.red}The PR requires a missing approval by a member of the smart-contract-core group (https://github.com/orgs/lifinance/teams/smart-contract-core).${colors.reset}`); - console.log(`${colors.red}Check failed.${colors.reset}`); - core.setFailed("Required approval is missing"); - } diff --git a/.github/workflows_deactivated/protectAuditCompletedLabel.yml b/.github/workflows_deactivated/protectAuditCompletedLabel.yml deleted file mode 100644 index 8a2b8e477..000000000 --- a/.github/workflows_deactivated/protectAuditCompletedLabel.yml +++ /dev/null @@ -1,78 +0,0 @@ -# - Protect "AuditCompleted" Label -# - makes sure that the label "AuditCompleted" can only be assigned by a Github action and not by a human actor -# - will undo any unauthorized change of this label -# - will fail if it runs into an error, otherwise pass - -name: Protect "AuditCompleted" Label - -on: - pull_request_target: #### << needs to be changed to 'pull_request' to activate it - types: [labeled, unlabeled] - -jobs: - protect_audit_label: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Log Event Payload - run: | - if [[ "${{ github.actor }}" == "github-actions" ]]; then - echo "This action was triggered by another GitHub Action." - else - echo "This action was triggered by a user: ${{ github.actor }}." - fi - echo "${{ github.event }}" - - - name: Check if "AuditCompleted" label was modified - env: - GITHUB_TOKEN: ${{ secrets.LIFI_GIT_ACTIONS_TOKEN }} - GH_PAT: ${{ secrets.LIFI_GIT_ACTIONS_TOKEN }} - run: | - # The label being monitored - TARGET_LABEL="AuditCompleted" - - # Check if the event was triggered by any other github action - if [[ "${{ github.actor }}" != "lifiGitActions" ]]; then #### TODO: REPLACE WITH GITHUB_ACTIONS_PAT and USERNAME <<<<<----------- - echo "This event was triggered by ${{ github.actor }}. Checking label..." - - # Determine if the label was added or removed - ACTION_TYPE="none" - if [[ "${{ github.event.action }}" == "labeled" && "${{ github.event.label.name }}" == "$TARGET_LABEL" ]]; then - ACTION_TYPE="added" - elif [[ "${{ github.event.action }}" == "unlabeled" && "${{ github.event.label.name }}" == "$TARGET_LABEL" ]]; then - ACTION_TYPE="removed" - fi - - # Revert the label change if necessary - if [[ "$ACTION_TYPE" != "none" ]]; then - echo -e "\033[31mUnauthorized modification of '$TARGET_LABEL' by ${{ github.actor }}. Reverting change...\033[0m" - - ##### remove or re-add label, depending on the case - if [[ "$ACTION_TYPE" == "added" ]]; then - # Remove the unauthorized label addition - gh pr edit ${{ github.event.pull_request.number }} --remove-label "$TARGET_LABEL" - elif [[ "$ACTION_TYPE" == "removed" ]]; then - # Re-add the unauthorized label removal - gh pr edit ${{ github.event.pull_request.number }} --add-label "$TARGET_LABEL" - fi - - # make sure that the label change was undone - CURRENT_LABELS=$(gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name') - if [[ "$ACTION_TYPE" == "added" && "$CURRENT_LABELS" == *"$TARGET_LABEL"* ]]; then - echo -e "\033[31Failed to remove the unauthorized 'AuditCompleted' label.\033[0m" - exit 1 - elif [[ "$ACTION_TYPE" == "removed" && "$CURRENT_LABELS" != *"$TARGET_LABEL"* ]]; then - echo -e "\033[31Failed to re-add the 'AuditCompleted' label.\033[0m" - exit 1 - fi - - echo -e "\033[32Unauthorized label modification was successfully prevented and undone.\033[0m" - else - echo -e "\033[32mNo unauthorized modifications detected.\033[0m" - fi - else - echo -e "\033[32mLabel change initiated by GitHub Action. No checks required.\033[0m" - fi diff --git a/audit/auditLog.json b/audit/auditLog.json index efed43665..50e1354a0 100644 --- a/audit/auditLog.json +++ b/audit/auditLog.json @@ -69,7 +69,7 @@ "auditorGitHandle": "sujithsomraaj", "auditReportPath": "./audit/reports/2024.12.03_LiFiDexAggregator.pdf", "auditCommitHash": "8a34562c912b5b19c919bb95338655c944428af5" - } + }, "audit20241205": { "auditCompletedOn": "05.12.2024", "auditedBy": "Sujith Somraaj (individual security researcher)", @@ -129,100 +129,188 @@ }, "auditedContracts": { "AcrossFacetV3": { - "1.0.0": ["audit20241007"], - "1.1.0": ["audit20250106"] + "1.0.0": [ + "audit20241007" + ], + "1.1.0": [ + "audit20250106" + ] }, "AcrossFacetPackedV3": { - "1.0.0": ["audit20241007"], - "1.2.0": ["audit20241206"] + "1.0.0": [ + "audit20241007" + ], + "1.2.0": [ + "audit20241206" + ] }, "CalldataVerificationFacet": { - "1.2.0": ["audit20240902"] + "1.2.0": [ + "audit20240902" + ] }, "DeBridgeDlnFacet": { - "1.0.0": ["audit20241205"] + "1.0.0": [ + "audit20241205" + ] }, "EmergencyPauseFacet": { - "1.0.0": ["audit20240913"], - "1.0.1": ["audit20241105"] + "1.0.0": [ + "audit20240913" + ], + "1.0.1": [ + "audit20241105" + ] }, "ERC20Proxy": { - "1.1.0": ["audit20250109_2"] + "1.1.0": [ + "audit20250109_2" + ] }, "Executor": { - "2.1.0": ["audit20250109_2"] + "2.1.0": [ + "audit20250109_2" + ] }, "FeeCollector": { - "1.0.1": ["audit20250109_3"] + "1.0.1": [ + "audit20250109_3" + ] }, "GasZipFacet": { - "2.0.0": ["audit20241107"], - "2.0.2": ["audit20250110_1"] + "2.0.0": [ + "audit20241107" + ], + "2.0.2": [ + "audit20250110_1" + ] }, "GasZipPeriphery": { - "1.0.0": ["audit20241107"], - "1.0.1": ["audit20250110_1"] + "1.0.0": [ + "audit20241107" + ], + "1.0.1": [ + "audit20250110_1" + ] }, "IAcrossSpokePool": { - "1.0.0": ["audit20250106"] + "1.0.0": [ + "audit20250106" + ] }, "IGasZip": { - "1.0.0": ["audit20241107"] + "1.0.0": [ + "audit20241107" + ] }, "LibAsset": { - "1.0.1": ["audit20241202"], - "1.0.2": ["audit20250110_1"] + "1.0.1": [ + "audit20241202" + ], + "1.0.2": [ + "audit20250110_1" + ] }, "LiFiDEXAggregator": { - "1.5.0": ["audit20241203"], - "1.6.0": ["audit20250109_2"], - "1.5.1": ["audit20250109_3"] + "1.5.0": [ + "audit20241203" + ], + "1.6.0": [ + "audit20250109_2" + ], + "1.5.1": [ + "audit20250109_3" + ] }, "LiFiTimelockController": { - "1.0.0": ["audit20250110_2"] + "1.0.0": [ + "audit20250110_2" + ] }, "LiFuelFeeCollector": { - "1.0.2": ["audit20250109_3"] + "1.0.2": [ + "audit20250109_3" + ] }, "Permit2Proxy": { - "1.0.0": ["audit20241122"], - "1.0.1": ["audit20250110_1"], - "1.0.2": ["audit20250109_3"] + "1.0.0": [ + "audit20241122" + ], + "1.0.1": [ + "audit20250110_1" + ], + "1.0.2": [ + "audit20250109_3" + ] }, "Receiver": { - "2.0.3": ["audit20250109_3"], - "2.1.0": ["audit20250109_2"] + "2.0.3": [ + "audit20250109_3" + ], + "2.1.0": [ + "audit20250109_2" + ] }, "ReceiverAcrossV3": { - "1.0.0": ["audit20241007"], - "1.0.1": ["audit20241206"], - "1.0.2": ["audit20250110_1"], - "1.1.0": ["audit20250109_2"] + "1.0.0": [ + "audit20241007" + ], + "1.0.1": [ + "audit20241206" + ], + "1.0.2": [ + "audit20250110_1" + ], + "1.1.0": [ + "audit20250109_2" + ] }, "ReceiverStargateV2": { - "1.0.1": ["audit20250109_3"], - "1.1.0": ["audit20250109_2"] + "1.0.1": [ + "audit20250109_3" + ], + "1.1.0": [ + "audit20250109_2" + ] }, "RelayerCelerIM": { - "1.0.3": ["audit20250109_3"], - "2.1.0": ["audit20250109_2"], - "2.1.1": ["audit20250109_3"] + "1.0.3": [ + "audit20250109_3" + ], + "2.1.0": [ + "audit20250109_2" + ], + "2.1.1": [ + "audit20250109_3" + ] }, "RelayFacet": { - "1.0.0": ["audit20241202"] + "1.0.0": [ + "audit20241202" + ] }, "StargateFacetV2": { - "1.0.1": ["audit20240814"] + "1.0.1": [ + "audit20240814" + ] }, "ThorSwapFacet": { - "1.2.1": ["audit20250109_1"] + "1.2.1": [ + "audit20250109_1" + ] }, "TokenWrapper": { - "1.0.1": ["audit20250109_3"], - "1.1.0": ["audit20250109_2"] + "1.0.1": [ + "audit20250109_3" + ], + "1.1.0": [ + "audit20250109_2" + ] }, "WithdrawablePeriphery": { - "1.0.0": ["audit20241014"] + "1.0.0": [ + "audit20241014" + ] } } } diff --git a/src/Facets/AcrossFacet.sol b/src/Facets/AcrossFacet.sol index 4736a7cb5..3cb75ca7f 100644 --- a/src/Facets/AcrossFacet.sol +++ b/src/Facets/AcrossFacet.sol @@ -13,7 +13,7 @@ import { Validatable } from "../Helpers/Validatable.sol"; /// @title Across Facet /// @author LI.FI (https://li.fi) /// @notice Provides functionality for bridging through Across Protocol -/// @custom:version 2.1.0 +/// @custom:version 2.0.0 contract AcrossFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { /// Storage /// @@ -46,8 +46,6 @@ contract AcrossFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable { wrappedNative = _wrappedNative; } - function bla() external {} - /// External Methods /// /// @notice Bridges tokens via Across