GitHub-এর CodeQL 2.26.0 একটি বিল্ট-ইন কুয়েরি যুক্ত করেছে যা AI prompt-injection প্যাটার্ন শনাক্ত করতে পারে, এবং এই পরিবর্তনের ফলে CI পাইপলাইনগুলো ইতিমধ্যেই নতুন ঝুঁকি চিহ্নিত করতে শুরু করেছে। শুধুমাত্র আপগ্রেড করাই যথেষ্ট নয়—টিমগুলোর এমন একটি রিগ্রেশন টেস্ট স্যুট প্রয়োজন যা কোড বিবর্তিত হওয়ার সাথে সাথে নিয়মটি কার্যকর থাকে তা নিশ্চিত করবে।

কেন একটি রিগ্রেশন ফিক্সচার (regression fixture) গুরুত্বপূর্ণ

প্রম্পট ইনজেকশন একজন আক্রমণকারীকে একটি প্রম্পটের মধ্যে ক্ষতিকারক নির্দেশাবলী ঢুকিয়ে দেওয়ার সুযোগ দেয় যা একটি ল্যাঙ্গুয়েজ মডেল পরবর্তীতে অনুসরণ করে। নতুন কুয়েরির মাধ্যমে, স্ট্যাটিক অ্যানালাইসিস একটি অনির্ভরযোগ্য উৎস (untrusted source) থেকে একটি মডেল-কলিং সিঙ্ক (model-calling sink) পর্যন্ত ডেটা ট্র্যাক করতে পারে। যদি নিয়মটি কেবল চালু করা হয় এবং কখনোই যাচাই করা না হয়, তবে পরবর্তীতে কোনো রিফ্যাক্টরিং ডেটা-ফ্লো পাথটি ভেঙে দিতে পারে এবং অ্যালার্টটি নিঃশব্দে হারিয়ে যেতে পারে। একটি রিগ্রেশন ফিক্সচার ঠিক সেই পথগুলো ক্যাপচার করে যা নিয়মটি ট্রিগার করা উচিত (বা করা উচিত নয়), যা স্ট্যাটিক-অ্যানালাইসিস ফলাফলকে একটি চুক্তিতে (contract) পরিণত করে যা বিল্ড এনফোর্স করে।

একটি নির্ভরযোগ্য ফিক্সচারের তিনটি উপাদান

  1. Untrusted source – যেকোনো ফাংশন যা বিশ্বস্ত কোডবেসের বাইরে থেকে ডেটা নিয়ে আসে (যেমন, একটি GitHub issue body, একটি webhook payload)।
  2. Prompt construction – সেই কোড যা মডেল রিকোয়েস্ট তৈরি করে, যা সাধারণত একটি ক্লায়েন্ট SDK কল।
  3. Model sink – সেই SDK মেথড যা প্রম্পটটি মডেলে পাঠায়। CodeQL-এর ডেটা-ফ্লো ইঞ্জিনকে সিঙ্কটি শনাক্ত করার জন্য আপনার প্রোডাকশন স্ট্যাক থেকে একটি রিয়েল কল দেখতে হবে।

শুধুমাত্র এই তিনটি উপাদান উপস্থিত থাকলেই কুয়েরিটি ফায়ার করবে।

টেস্ট ফাইলগুলো সাজানো

একটি প্রচলিত লেআউট টেস্ট স্যুটটিকে অডিট করা সহজ রাখে:

security-fixtures/prompt-injection/
├─ positive/
│  ├─ direct-flow.ts
│  └─ helper-flow.ts
├─ negative/
│  └─ trusted-instruction.ts
└─ expected-alerts.json

Positive ফাইলগুলোতে এমন কোড থাকে যা ফ্ল্যাগ করা উচিত; negative ফাইলগুলোতে নিরাপদ প্যাটার্ন থাকে যা সাইলেন্ট থাকা আবশ্যক।

পজিটিভ কেসগুলো (positive cases) লেখা

সবচেয়ে সহজ উদাহরণটি একটি অনির্ভরযোগ্য ভ্যালু থেকে সরাসরি মডেল কলে ডেটা প্রবাহ দেখায়:

import { model } from "./supported-client";

declare function loadIssueBody(id: number): Promise<string>;

export async function summarize(id: number) {
  const untrusted = await loadIssueBody(id);
  return model.generate({
    system: "Summarize the issue",
    user: untrusted,
  });
}

এখানে loadIssueBody হলো অনির্ভরযোগ্য উৎস, model.generate হলো সিঙ্ক, এবং ডেটা কোনো স্যানিটাইজেশন ধাপ ছাড়াই পাস হচ্ছে—ঠিক যা কুয়েরিটি ধরার জন্য ডিজাইন করা হয়েছে।

দ্বিতীয় একটি পজিটিভ কেস ডেটাটিকে একটি হেল্পার ফাংশনের মাধ্যমে রাউট করা উচিত, যা প্রমাণ করে যে অ্যানালাইসিসটি পরোক্ষ পথগুলো অনুসরণ করতে পারে:

function wrapUserInput(input: string) {
  return { system: "Summarize the issue", user: input };
}

export async function summarizeViaHelper(id: number) {
  const raw = await loadIssueBody(id);
  return model.generate(wrapUserInput(raw));
}

উভয় ফাইলই positive/ এর অধীনে থাকবে।

নেগেটিভ কেস (negative case) লেখা

নেগেটিভ ফিক্সচারটিকে অবশ্যই প্রদর্শন করতে হবে যে ইউজার ইনপুট মডেলের নির্দেশাবলী পরিবর্তন করতে পারে না। একটি সাধারণ ভুল হলো ধরে নেওয়া যে sanitize() নামক একটি ফাংশন নিরাপত্তা নিশ্চিত করে। স্ট্যাটিক অ্যানালাইজার নামটিকে প্রমাণ হিসেবে গণ্য করে না, তাই টেস্টটিতে কোনো বিভ্রান্তিকর স্যানিটাইজেশন স্টাব (sanitisation stub) এড়িয়ে চলা উচিত:

export async function safeSummarize(id: number) {
  const trusted = "Summarize the issue";
  const user = await loadIssueBody(id); // not used in the system prompt
  return model.generate({
    system: trusted,
    user: "Static placeholder",
  });
}

যেহেতু অনির্ভরযোগ্য ডেটা কখনোই system ফিল্ডে পৌঁছায় না, তাই নিয়মটি সাইলেন্ট থাকা উচিত।

JSON-এ প্রত্যাশাগুলো (expectations) ঘোষণা করা

স্যুটটির চুক্তি expected-alerts.json-এ থাকে। এটি প্রয়োজনীয় অ্যালার্ট এবং স্পষ্টভাবে নিষিদ্ধ পথগুলো তালিকাভুক্ত করে:

{
  "required": [
    {
      "ruleId": "USE_ACTUAL_RULE_ID",
      "pathSuffix": "positive/direct-flow.ts"
    },
    {
      "ruleId": "USE_ACTUAL_RULE_ID",
      "pathSuffix": "positive/helper-flow.ts"
    }
  ],
  "forbiddenPathSuffixes": [
    "negative/trusted-instruction.ts"
  ]
}

USE_ACTUAL_RULE_ID-এর পরিবর্তে CodeQL ডকুমেন্টেশন বা SARIF আউটপুটে দেখানো আইডেন্টিফায়ারটি ব্যবহার করুন। আইডি অনুমান করবেন না; CI চেকের জন্য সঠিক স্ট্রিংটি অত্যন্ত গুরুত্বপূর্ণ।

CI-এর সাথে ফিক্সচারটি যুক্ত করা

  1. পাইপলাইনে ব্যবহৃত CodeQL CLI ভার্সনটি 2.26.0 (বা তার পরবর্তী) ভার্সনে পিন করুন।
  2. ফিক্সচার চালানোর আগে বর্তমান চেকআউট থেকে একটি ডিসপোজেবল ডেটাবেস তৈরি করুন।
  3. কুয়েরিটি চালান, অ্যালার্টগুলো ক্যাপচার করুন এবং সেগুলোকে expected-alerts.json-এর সাথে তুলনা করুন।
  4. যদি কোনো প্রয়োজনীয় অ্যালার্ট হারিয়ে যায় বা কোনো নিষিদ্ধ পথ অ্যালার্ট দিতে শুরু করে, তবে বিল্ডটি ফেইল করুন।

পুরো রিপোজিটরির মোট অ্যালার্ট সংখ্যা যাচাই করবেন না—অপ্রাসঙ্গিক পরিবর্তন সংখ্যা বাড়িয়ে দিতে পারে এবং ভুলভাবে ফেইল ঘটাতে পারে।

আপগ্রেডের পর যা খেয়াল রাখতে হবে

যখন আপনি CodeQL-কে নতুন ভার্সনে আপডেট করবেন:

  • প্রয়োজনীয় অ্যালার্ট এখনও দেখা যাচ্ছে – স্বাভাবিক রিভিউ চালিয়ে যান।
  • প্রয়োজনীয় অ্যালার্ট অদৃশ্য হয়ে গেছে – বিল্ড ব্লক করুন; তদন্ত করুন যে নতুন ভার্সনটি কুয়েরি লজিক পরিবর্তন করেছে কি না অথবা কোড পরিবর্তনের কারণে ডেটা ফ্লো ভেঙে গেছে কি না।
  • নতুন পজিটিভ লোকেশন দেখা যাচ্ছে – এটি একটি প্রকৃত ইনজেকশন পাথ কিনা তা নিশ্চিত করার পরে required লিস্টে যোগ করুন।
  • নেগেটিভ কন্ট্রোল অ্যালার্ট দিতে শুরু করেছে – প্রশমন কৌশলটি (mitigation strategy) পুনরায় দেখুন; নিয়মটি আরও কঠোর হয়ে থাকতে পারে।

স্ট্যাটিক অ্যানালাইসিস রানটাইমে মডেলটি কীভাবে প্রতিক্রিয়া জানাবে তা প্রমাণ করতে পারে না। রিগ্রেশন স্যুটটিকে অ্যাডভারসারিয়াল টেস্টের মাধ্যমে পরিপূরক করুন যা প্রকৃতপক্ষে মডেলে তৈরি করা প্রম্পট পাঠায় এবং রেসপন্স যাচাই করে।

সারসংক্ষেপ (Takeaway)

CodeQL 2.26.0 আপনাকে শিপ করার আগেই প্রম্পট-ইনজেকশন বাগ ধরার ক্ষমতা দেয়, তবে শুধুমাত্র তখনই যখন আপনি একটি ফোকাসড রিগ্রেশন ফিক্সচারের মাধ্যমে সেই ক্ষমতাটি লকড করবেন। অনির্ভরযোগ্য উৎস, রিয়েল SDK সিঙ্ক এবং একটি JSON চুক্তিতে স্পষ্ট প্রত্যাশা সংজ্ঞায়িত করার মাধ্যমে, আপনি একটি স্ট্যাটিক-অ্যানালাইসিস নিয়মকে একটি গেটে পরিণত করেন যা রিগ্রেশন রোধ করে এবং দ্রুত বিবর্তিত অ্যাটাক সারফেসের ওপর ক্রমাগত মনোযোগ নিশ্চিত করে।