প্রযুক্তি ও উদ্ভাবন

GitHub Actions cache-mode বসান—একটি ভুলে cache poisoning ফিরতে পারে

|লেখক: QUASA সম্পাদকীয় দল|5 মিনিটের পাঠ| 1
GitHub Actions cache-mode বসান—একটি ভুলে cache poisoning ফিরতে পারে

GitHub Actions-এ নিরাপদ baseline হলো workflow-এর top level-এ cache-mode: read বসানো। এরপর শুধু যাচাই করা trusted producer job-কে write, পুরোনো cache না-পড়া producer-কে write-only এবং cache অপ্রয়োজনীয় job-কে none দিন।

বিশেষ করে pull_request_target, issue_comment বা workflow_run-এর মতো low-trust trigger-এ write বা write-only দিলে GitHub-এর read-only default অতিক্রম করা হয়। সেই job untrusted input থেকে cache লিখতে পারলে পরে privileged workflow বিষাক্ত cache restore করতে পারে—শিরোনামের ভুলটি এই explicit override।

চারটি mode-এর সীমা বুঝে নিন

Workflow-level মান সব job-এ প্রযোজ্য হয়; jobs.<job_id>.cache-mode দিলে শুধু সেই job-এ মানটি বদলে যায়। GitHub-এর workflow syntax চারটি অনুমোদিত mode এবং restore-save ক্ষমতার পার্থক্য নির্দিষ্ট করেছে।

  • read: cache restore করা যায়, save করা যায় না। পরীক্ষা বা low-trust consumer job-এর উপযোগী baseline।
  • write: restore ও save দুটিই করা যায়। trusted trigger-এর সীমিত producer job-এ ব্যবহার করুন।
  • write-only: আগের cache restore না করে নতুন cache save করা যায়। clean rebuild থেকে cache প্রকাশ করা job-এ এটি কাজে লাগে।
  • none: restore ও save দুটিই বন্ধ। cache লাগে না এমন policy, lint বা sensitive deployment job-এ এটি স্পষ্ট সীমা দেয়।

২০২৬ সালের ১০ সেপ্টেম্বর প্রকাশিত GitHub-এর ঘোষণায় সুবিধাটিকে GitHub.com-এর সব plan-এ generally available বলা হয়েছে। একই ঘোষণায় জানানো হয়, low-trust event-এ write-capable mode দিলে warning annotation দেখা যায়; warning run থামায় না, তাই সফল status-কে নিরাপদ configuration-এর প্রমাণ ভাববেন না।

Top-level read, producer job-এ write বসান

GitHub Actions YAML-এ top-level read এবং main branch producer job-এ write override

একটি workflow-তে pull request পরীক্ষা ও main branch-এর cache producer থাকলে top level-এ read রাখুন। Producer job-এর if condition এবং write override একই job-এ রাখলে pull request run সেই write ক্ষমতা পায় না।

name: CI

on: [push, pull_request]

cache-mode: read

jobs:

  test:

    runs-on: ubuntu-latest

    steps:

      - uses: actions/checkout@v6

      - uses: actions/cache@v4

        with:

          path: ~/.npm

          key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}

      - run: npm ci

  warm-cache:

    if: github.event_name == 'push' && github.ref == 'refs/heads/main'

    runs-on: ubuntu-latest

    cache-mode: write

    steps:

      - uses: actions/checkout@v6

      - uses: actions/cache@v4

        with:

          path: ~/.npm

          key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}

      - run: npm ci

নিজের repository-তে branch-এর নাম, package manager, cache path ও key বদলাতে হবে। শুধু if condition লিখে top-level write রাখা যথেষ্ট নয়: তখন workflow-এর অন্য job-ও write access পেতে পারে।

Event নয়, input ও cache scope মিলিয়ে mode বাছুন

GitHub-এর dependency caching reference অনুযায়ী default-branch scope-এ push, workflow_dispatch, repository_dispatch, delete, registry_package, page_buildschedule cache তৈরি বা overwrite করতে পারে; অন্য trigger default branch-এ resolve করলে read-only access পায়। pull_request আলাদা: তার cache merge ref-এ scoped, তাই সেটি default-branch cache লিখতে পারে না।

  • pull_request_target, issue_comment, workflow_run: read রাখুন। Job untrusted checkout, comment text বা pull request data চালালে write-capable override দেবেন না।
  • pull_request: cache save জরুরি না হলে read লিখে intent স্পষ্ট করুন, যদিও এর cache merge ref-এ সীমাবদ্ধ।
  • protected main-এ push: dependency cache বজায় রাখা producer job-এ write দিন; একই workflow-এর cache-বিহীন job-এ none ব্যবহার করুন।
  • workflow_dispatch বা schedule: trigger অনুমোদিত হলেও checkout ref, inputs এবং job কী চালায় তা না দেখে write দেবেন না।
  • clean publisher: পুরোনো cache না-পড়ে সম্পূর্ণ rebuild করলে write-only বিবেচনা করুন।

Cache contents signed বা verified নয়। তাই write অনুমোদনের সিদ্ধান্তে শুধু event-এর নাম নয়, job কোন code ও input চালায় এবং পরের privileged run সেই cache restore করবে কি না—এই সম্পূর্ণ পথটি বিবেচনা করুন।

Reusable workflow-র caller-এ cap বসান

caller-এর read সীমায় reusable workflow-এর অতিরিক্ত write access বন্ধ

Reusable workflow call করা job-এ explicit mode দিন। Caller job কোনো explicit mode না পেলে called workflow low-trust trigger-এর default read থাকা সত্ত্বেও নিজে write চাইতে পারে; কিন্তু caller-এ cache-mode: read থাকলে callee সেই সীমা অতিক্রম করতে পারে না।

jobs:

  shared-tests:

    cache-mode: read

    uses: octo-org/ci/.github/workflows/test.yml@pinned-commit-sha

Called workflow সীমার বেশি access চাইলে run শুরু হয় না; GitHub validation error দেয়। readwrite-only উচ্চ-নিম্ন ক্রমের mode নয়—প্রথমটি restore এবং দ্বিতীয়টি save দেয়, তাই caller ও callee-র মধ্যে এই দুই মানের অমিলও অতিরিক্ত access request হিসেবে ধরা হয়।

Nested reusable workflow থাকলে call chain-এর প্রতিটি স্তরে effective cap যাচাই করুন। Shared workflow বদলানোর review-তে তার caller files-ও রাখুন, কারণ callee-র নতুন cache দাবি পুরোনো caller limit-এর সঙ্গে অসামঞ্জস্যপূর্ণ হতে পারে।

ACTIONS_CACHE_MODE দিয়ে effective access যাচাই করুন

ACTIONS_CACHE_MODE read থাকায় save বাদ গেলেও সফলভাবে চলা workflow

YAML-এ প্রত্যাশিত মান লেখা আছে কি না দেখাই যথেষ্ট নয়; runner কোন effective mode পেয়েছে সেটি পরীক্ষা করুন। অস্থায়ী diagnostic step হিসেবে run: echo "ACTIONS_CACHE_MODE=$ACTIONS_CACHE_MODE" যোগ করতে পারেন, অথবা pull request consumer-এ test "$ACTIONS_CACHE_MODE" = "read" দিয়ে assertion চালাতে পারেন।

Mode restore অনুমোদন না করলে operation skip হয়ে cache miss হিসেবে গণ্য হয়; save অনুমোদিত না হলে save-টি করা হয় না। এ কারণে step, job বা workflow ব্যর্থ হয় না। তাই migration-এর পরে cache-hit false হওয়া বা build ধীর হওয়াকে সরাসরি permission failure ধরে নেবেন না—আগে effective mode, event, job-level override এবং cache key মিলিয়ে দেখুন।

Low-trust run-এ তিনটি সংকেত খুঁজুন: write access-এর warning annotation, অপ্রত্যাশিত ACTIONS_CACHE_MODE=write বা write-only, এবং cache save করার log। কোনো secret, token বা সম্পূর্ণ environment diagnostic output-এ প্রকাশ করবেন না।

Migration ও rollback checklist

  1. .github/workflows-এ actions/cache, package manager-এর integrated caching এবং reusable workflow call খুঁজুন। প্রতিটি job restore করে, save করে, নাকি cache ব্যবহারই করে না—তা চিহ্নিত করুন।
  2. Top level-এ cache-mode: read দিন। Cache-বিহীন job-এ none এবং যাচাই করা trusted producer-এ job-level write বা write-only বসান।
  3. প্রতিটি reusable workflow caller job-এ explicit cap দিন; trigger-based default-কে caller limit ধরে নেবেন না।
  4. Pull request, main push এবং প্রযোজ্য manual বা scheduled run চালিয়ে ACTIONS_CACHE_MODE, warning এবং cache action log পরীক্ষা করুন। Expected miss-কে failure হিসেবে চিহ্নিত করবেন না।
  5. ছোট batch-এ workflow migrate করুন, যাতে validation error বা performance change কোন mode পরিবর্তন থেকে এসেছে তা আলাদা করা যায়।

Low-trust run-এ accidental write ধরা পড়লে প্রথমে সংশ্লিষ্ট job-কে read বা none-এ ফেরান। সন্দেহজনক cache privileged run-এ restore না করে cache key বা version বদলান এবং trusted producer থেকে নতুন cache তৈরি করুন।

Reusable workflow validation error দিলে নিরাপত্তা cap পুরোপুরি সরাবেন না। Callee-র দাবি কমান; কেবল প্রয়োজন প্রমাণিত হলে নির্দিষ্ট trusted caller-এর সীমা বাড়ান, তারপর effective mode আবার যাচাই করুন।

আরও পড়ুন:

শেয়ার করুন:

আমাদের নিউজলেটার নিন

সর্বশেষ Web3, AI ও ক্রিপ্টো সংবাদ সরাসরি আপনার ইনবক্সে পান।

0