◆ مهندسی هوش مصنوعی با Codex

ماژول ۰ · هفته ۱ · حدود ۳ ساعت

#ماژول ۰. راه‌اندازی و خط پایه

بخشی از صفحه اصلی دوره. حدود ۳ ساعت: ۱ ساعت خواندن، ۲ ساعت کار عملی. بررسی‌شده با Codex CLI 0.167.1 در ۱۱ نوامبر ۲۰۲۶.

#شروع سریع (۱۵ دقیقه)

قبل از هر چیز دیگری این را انجام بده. یک عدد پایه به دست می‌آوری که بعداً خودت را با آن مقایسه کنی.

تو مهندس امنیتی، پس با همان کاری شروع کن که با هر ابزار تازه روی لپ‌تاپت می‌کنی: audit. ترمینال را باز کن و به هر چک جواب بله یا نه بده. هر بله یک امتیاز دارد.

bash
codex --version                                   # 1. at least 0.149.0?
ls -l ~/.codex/auth.json 2>/dev/null              # 3. no plaintext auth file?
grep -nE 'yolo|dangerously|full-auto' ~/.zshrc ~/.zprofile ~/.bashrc 2>/dev/null   # 4. no bypass aliases?
grep -nE 'sandbox_mode|approval_policy|web_search|trust_level|shell_environment_policy|metrics_exporter' ~/.codex/config.toml 2>/dev/null
codex mcp list                                    # 10. every server one you recognise?
  1. نسخه CLI دست‌کم 0.149.0 است. نسخه‌های قدیمی‌تر راه‌های شناخته‌شده‌ای برای فرار از سندباکس دارند (مفهوم ۱ را ببین).
  2. افزونه VS Code (openai.chatgpt) به‌روز است.
  3. فایل ~/.codex/auth.json وجود ندارد، چون credentialها به جای یک فایل plaintext در Keychain مک ذخیره می‌شوند.
  4. در شل هیچ alias یا تابعی نیست که --yolo، --dangerously-bypass-approvals-and-sandbox یا --full-auto قدیمی را اضافه کند.
  5. در کانفیگ کاربری‌ات sandbox_mode روی danger-full-access نیست.
  6. در کانفیگ کاربری‌ات approval_policy روی never نیست.
  7. مقدار پیش‌فرض web_search روی live نیست.
  8. هر ورودی trust_level = "trusted" مال ریپویی است که خودت صاحبش هستی و آن را خوانده‌ای.
  9. یک بخش [shell_environment_policy] داری که نمی‌گذارد توکن‌ها و credentialهای کلاد به دستورهایی که Codex اجرا می‌کند برسند.
  10. همه MCP serverهای خروجی codex mcp list را خودت و عمداً اضافه کرده‌ای.

خط پایه:

  • تاریخ:
  • نسخه Codex:
  • امتیاز audit:
  • چکی که بیشتر از همه غافلگیرت کرد:

بیشتر کسانی که چند ماه پیش Codex را نصب کرده‌اند ۳ تا ۵ می‌گیرند. تنظیمات پیش‌فرض بد نیست، ولی جای ذخیره auth، متغیرهای محیطی و فولدرهای trusted را به خودت سپرده‌اند. آخر این ماژول باید امتیازت ۱۰ باشد، یا برای هر «نه» دلیلی نوشته باشی.

#چرا مهم است

چیزی که روی لپ‌تاپت نصب کرده‌ای یک ایجنت است با دسترسی به شل. ریپوهایت را می‌خواند، دستور اجرا می‌کند، پکیج نصب می‌کند و به شبکه وصل می‌شود. بیشتر این کارها را هم با یوزر خودت انجام می‌دهد. همه ماژول‌های بعدی فرض می‌کنند می‌دانی به چه چیزهایی دسترسی دارد و به چه چیزهایی ندارد.

قضیه یک طرف دیگر هم دارد. از ماژول ۱ تا ۸ یک پروژه به اسم sentinel می‌سازی. یک ابزار کوچک TypeScript است که مانیفست‌های Kubernetes و تغییرات JavaScript را از نظر امنیتی ریویو می‌کند. در این ماژول ریپو و داده مرجع (ground truth) آن را می‌سازی، یعنی چند فایل که مشکلشان را از قبل می‌دانی و یک فایل برچسب که دقیقاً می‌گوید هر فایل چه مشکلی دارد. همه evalهای بعدی دوره با همین برچسب‌ها سنجیده می‌شوند. پس باید قبل از اینکه هیچ مدلی به آن‌ها دست بزند آماده باشند.

#تصویر کلی

flowchart LR
  subgraph You["Your Mac"]
    IDE["VS Code extension<br/>openai.chatgpt"] --> CFG["~/.codex/config.toml<br/>+ profiles, rules, hooks"]
    CLI["codex CLI"] --> CFG
    CFG --> SB["Sandbox<br/>(Seatbelt on macOS)"]
    SB --> REPO["Your repo<br/>(workspace)"]
  end
  CLI -- "ChatGPT sign-in<br/>(plan limits)" --> OAI["OpenAI"]
  CLOUD["Codex Cloud<br/>tasks in OpenAI VMs"] --> OAI
  CODE["Your own code<br/>(modules 5-8)"] -- "API key<br/>(separate billing)" --> OAI

دو مسیر جدا به OpenAI می‌رسد. خود Codex، چه CLI باشد چه IDE چه کلاد، با اکانت ChatGPT تو لاگین می‌کند و از سقف پلنت کم می‌کند. ولی کدی که از ماژول ۵ به بعد خودت می‌نویسی با یک API key کار می‌کند که صورت‌حساب pay-as-you-go جدا دارد. این دو را با هم قاطی نکن.

#مفهوم ۱. رابط‌ها، نسخه‌ها و حداقل نسخه مجاز

یک Codex داری با چند رابط مختلف، و همه این رابط‌ها از یک دایرکتوری کانفیگ مشترک استفاده می‌کنند: ~/.codex/.

رابط چیست نکته‌ها
خط فرمان (CLI) باینری codex که با Rust نوشته شده نصب با brew install --cask codex یا npm install -g @openai/codex. آپدیت با codex update
افزونه VS Code شناسه Marketplace آن openai.chatgpt است با CLI از همان فایل ~/.codex/config.toml استفاده می‌کند. فایل‌های باز و متن انتخاب‌شده را به کانتکست اضافه می‌کند. diff کناری و checkpointهای Git دارد و می‌شود از داخلش کار را به کلاد سپرد
اپ دسکتاپ الان بخشی از اپ دسکتاپ ChatGPT است با codex app باز می‌شود. با /app هم می‌توانی یک سشن CLI را به آنجا ببری
سرویس Codex Cloud تسک‌ها در VMهای OpenAI اجرا می‌شوند، هر کدام در workspace خودش تسک را از وب، موبایل، IDE یا codex cloud شروع می‌کنی. با codex apply نتیجه را به ریپوی محلی‌ات می‌آوری
گیت‌هاب نوشتن @codex review روی یک pull request در ماژول ۴ به آن می‌رسیم

آدرس مستندات در ۲۰۲۶ عوض شد. لینک‌های قدیمی developers.openai.com/codex/... حالا به learn.chatgpt.com/docs/... ریدایرکت می‌شوند و همه لینک‌های این دوره آدرس جدید را دارند.

حداقل نسخه مجاز. Codex چند بار پشت سر هم باگ فرار از سندباکس داشته و تقریباً همه‌شان یک الگو دارند: چیزی داخل ریپو که دست مهاجم است بیرون از سندباکس اجرا می‌شود. چند نمونه:

  • آسیب‌پذیری CVE-2025-61260. فایل .codex/config.toml خود ریپو می‌توانست یک MCP server تعریف کند که موقع اجرا بالا می‌آمد. امروز جلوی این را همان سؤالی می‌گیرد که Codex برای trust کردن پروژه می‌پرسد (مفهوم ۳).
  • آسیب‌پذیری CVE-2026-19592. خود Codex موقع چک کردن Git status، برنامه core.fsmonitor را که در ریپو تعریف شده بود اجرا می‌کرد، آن هم بیرون از سندباکس. در 0.131.0 درست شد.
  • باگ Overpatch (اوت ۲۰۲۶ گزارش شد و CVE ندارد). ترکیب apply_patch با یک symlink می‌توانست چیزی به ته ~/.zshrc اضافه کند. در 0.149.0 درست شد. منبع این نسخه یک یادداشت تحقیقاتی Cloud Security Alliance است و advisory خود OpenAI نیست.

پس حداقل نسخه 0.149.0 است. این را هم عادت کن: قبل از باز کردن ریپویی که خودت ننوشته‌ای، آپدیت کن. ماژول ۲ مفصل‌تر سراغ این الگو می‌رود. فعلاً فقط آپدیت کن.

#مفهوم ۲. دو نوع credential

لاگین با ChatGPT همان روشی است که Codex با آن روی پلن Plus یا Pro تو کار می‌کند. با codex login یک مرورگر باز می‌شود. روی ماشینی که مرورگر ندارد codex login --device-auth را بزن.

جای ذخیره توکن مهم است. در حالت پیش‌فرض ممکن است توکن به صورت plaintext در ~/.codex/auth.json نوشته شود. آن وقت هر چیزی که بتواند فولدر home تو را بخواند می‌تواند از اکانت ChatGPT تو استفاده کند، مثلاً یک اسکریپت postinstall مخرب در یک پکیج npm. روی macOS توکن را در Keychain نگه دار:

toml
cli_auth_credentials_store = "keyring"

بعد یک بار codex logout و دوباره codex login بزن و چک کن ~/.codex/auth.json پاک شده باشد.

یک OpenAI API key چیز دیگری است و صورت‌حساب جدا دارد. از ماژول ۵ که کد خودت Responses API را صدا می‌زند لازمش داری. پلن ChatGPT هزینه API را پوشش نمی‌دهد. همین الان آماده‌اش کن و برایش سقف هزینه بگذار:

  1. در پلتفرم OpenAI یک پروژه به اسم sentinel بساز.
  2. برای آن پروژه بودجه ماهانه ۲۰ دلار بگذار.
  3. یک key بساز که فقط به همان پروژه محدود باشد. آن را در password manager نگه دار و هیچ وقت در فایلی داخل ریپو نگذار.

در پروفایل شلت OPENAI_API_KEY را ست نکن. اگر در environment تو باشد، هر دستوری که Codex اجرا می‌کند آن را می‌بیند، مگر اینکه فیلترش کرده باشی (مفهوم ۴). هر وقت لازمش داشتی، فقط برای همان دستور با CLI همان password manager لودش کن.

#مفهوم ۳. لایه‌های کانفیگ و اعتماد به پروژه

تنظیمات Codex از چند لایه خوانده می‌شود. به ترتیب از کمترین اولویت تا بیشترین:

  1. کاربر: ~/.codex/config.toml.
  2. پروفایل: ~/.codex/<name>.config.toml، که با codex --profile <name> فعال می‌شود.
  3. پروژه: .codex/config.toml داخل ریپو، از ریشه تا فولدر فعلی. فقط در پروژه‌های trusted.
  4. خط فرمان: -c key=value و فلگ‌های مخصوص مثل -m و -s.

دو تغییر در ۲۰۲۶ باعث شده راهنماهای قدیمی‌تر غلط از آب دربیایند:

  • پروفایل‌ها حالا فایل جدا هستند. از 0.134.0 دیگر جدول‌های [profiles.<name>] و کلید سطح بالای profile = "..." کار نمی‌کنند. هر پروفایل یک فایل جداست، مثل ~/.codex/review.config.toml، و داخلش همان کلیدهای معمولی سطح بالا را می‌نویسی.
  • بعضی کلیدها در کانفیگ پروژه عمداً نادیده گرفته می‌شوند: model provider و base URLها، notify، پروفایل‌ها و otel. پس یک ریپوی مخرب نمی‌تواند Codex را به endpoint مدل خودش بفرستد یا telemetry تو را جای دیگری ببرد. ولی هنوز می‌تواند برای آن پروژه مدل، حالت سندباکس و approval policy را عوض کند. برای همین اعتماد به پروژه مهم است.

اعتماد (trust). اولین بار که یک فولدر را باز می‌کنی، Codex می‌پرسد به آن اعتماد داری یا نه. اگر پروژه trusted باشد، Codex فولدر .codex/ آن را لود می‌کند، یعنی کانفیگ، hookها و ruleها. اگر untrusted باشد، همه این‌ها کنار گذاشته می‌شوند و ممکن است Codex فقط‌خواندنی شروع شود. وضعیت trust در کانفیگ کاربری تو ذخیره می‌شود:

toml
[projects."/Users/you/src/sentinel"]
trust_level = "trusted"

سؤال «به این فولدر اعتماد داری؟» را مثل chmod +x روی یک اسکریپت دانلودشده جدی بگیر. اول فولدر .codex/ و فایل AGENTS.md ریپو را بخوان. اگر ریپو را همین الان برای ریویو کلون کرده‌ای، دقیقاً جایی است که باید نه بگویی.

#مفهوم ۴. سکرت‌ها را از شل ایجنت دور نگه دار

دستورهایی که Codex اجرا می‌کند به طور پیش‌فرض یک کپی فیلترشده از environment تو را به ارث می‌برند. این فیلتر را خودت صریح بنویس تا توکن‌ها و credentialهای کلاد هیچ وقت به دستوری که مدل انتخاب کرده نرسند:

toml
[shell_environment_policy]
inherit = "core"                 # only a core set (PATH, HOME, ...), not everything
ignore_default_excludes = false  # keep the built-in excludes too

[shell_environment_policy.filters]
"*_TOKEN" = "exclude"
"*_SECRET*" = "exclude"
"*_KEY" = "exclude"
"AWS_*" = "exclude"
"KUBECONFIG" = "exclude"

این تنظیم فقط به environment مربوط است. فایل‌ها حسابشان جداست. در سندباکس پیش‌فرض، Codex می‌تواند بیشتر دیسک تو را بخواند، از جمله ~/.kube/config و ~/.aws/credentials. سندباکس نوشتن را محدود می‌کند و کاری به خواندن ندارد. قانون‌های deny برای هر مسیر را در ماژول ۲ با permission profileها می‌نویسی. قانون این هفته ساده است: روی ماشینی که ایجنت را اجرا می‌کنی credential زنده production نگه ندار، یا آن را پشت یک لاگین کوتاه‌مدت بگذار، مثل SSO، aws sso login یا kubelogin.

#مفهوم ۵. مدل‌ها، سطح استدلال (effort) و سقف پلن

با codex debug models فهرست مدل‌هایی را می‌بینی که CLI تو واقعاً استفاده می‌کند. در ۱۱ نوامبر ۲۰۲۶ مدل‌های اصلی این‌ها بودند:

مدل برای چه کاری
مدل gpt-6-astra برای سخت‌ترین مسئله‌ها. سقف پلن را سریع مصرف می‌کند
مدل gpt-6.1-sol پیش‌فرض کارهای روزمره. کیفیتش نزدیک Astra است و هزینه‌اش خیلی کمتر
مدل gpt-6.1-terra کارهای سریع مثل نوشتن، vision و ویرایش‌های کوچک. CLI نسخه 0.159.0 یا بالاتر می‌خواهد
مدل gpt-6-luna کارهای ساده و پرتعداد، مثل subagentهایی که فایل جست‌وجو می‌کنند

سطح استدلال (reasoning effort) تنظیم جدایی است. مقدارهایش low، medium، high، xhigh و max است. بیشتر مدل‌ها ultra هم دارند که به ایجنت اجازه می‌دهد کار را به subagentها بسپارد. هر چه effort بالاتر باشد کار کندتر پیش می‌رود و سقفت زودتر تمام می‌شود. داخل سشن با /model عوضش می‌کنی، در خط فرمان با -m و -c model_reasoning_effort="high"، و در کانفیگ با model و model_reasoning_effort.

پلن Plus سقف‌های پنج‌ساعته دارد که برای هر مدل فرق می‌کند. صفحه قیمت‌ها برای تعداد پیام‌های محلی در هر پنج ساعت این بازه‌ها را داده بود: Astra بین ۵ تا ۴۵، 6.1 Sol بین ۱۵ تا ۱۶۰، 6.1 Terra بین ۳۵ تا ۲۸۵ و Luna بین ۳۵۰ تا ۳۰۰۰. پلن‌های Pro «در حال حاضر سقف پنج‌ساعته ندارند». با /status می‌بینی چقدر از سقفت مانده. برای همین دوره اجراهای تکراری را روی ۵ گذاشته و کارهای ارزان را به Luna می‌سپارد.

#مثال کامل: یک کانفیگ اولیه هاردن‌شده

این یک ~/.codex/config.toml معقول برای این دوره است. فقط چیزهایی را عوض می‌کند که دلیلشان در مفهوم ۲ تا ۵ آمد. سندباکس را هم روی حالت پیش‌فرض نگه می‌دارد، یعنی workspace-write که قبل از بیرون رفتن از سندباکس سؤال می‌پرسد. ماژول ۲ سندباکس را محدودتر می‌کند.

toml
# ~/.codex/config.toml  — checked against Codex CLI 0.167.1
model = "gpt-6.1-sol"
model_reasoning_effort = "medium"
approval_policy = "on-request"         # Codex asks before leaving the sandbox
web_search = "cached"                  # OpenAI's index, not live fetches: less injection exposure
cli_auth_credentials_store = "keyring" # macOS Keychain, not plaintext auth.json

[shell_environment_policy]
inherit = "core"
ignore_default_excludes = false

[shell_environment_policy.filters]
"*_TOKEN" = "exclude"
"*_SECRET*" = "exclude"
"*_KEY" = "exclude"
"AWS_*" = "exclude"
"KUBECONFIG" = "exclude"

[history]
persistence = "save-all"               # you will read your own sessions in module 3

[analytics]
enabled = false

[otel]
metrics_exporter = "none"              # the default sends metrics to statsig

یک پروفایل هم برای بعد بساز و با اسم ~/.codex/deep.config.toml ذخیره‌اش کن:

toml
# codex --profile deep   — for the hard problems only
model = "gpt-6-astra"
model_reasoning_effort = "high"

بعد چک کن که درست خوانده می‌شود:

bash
codex doctor            # config line must say "✓ config loaded", not "⚠ config"
codex --strict-config   # refuses to start on an unknown key; then type /debug-config and /status

در خروجی /debug-config هر مقداری را که ست کرده‌ای پیدا کن و ببین نوشته از کانفیگ کاربری تو آمده یا نه.

#مثال کامل: ریپو sentinel و داده مرجع آن

قرار است sentinel دو نوع ورودی را ریویو کند: مانیفست‌های Kubernetes و کد JavaScript. پیش از اینکه هیچ مدلی سراغ این ورودی‌ها برود، باید چند نمونه داشته باشی که جوابشان را از قبل می‌دانی. فقط این‌طوری بعداً می‌توانی بسنجی مدل مشکلات واقعی را پیدا کرده، بعضی را جا انداخته یا چیزی از خودش درآورده.

اسکریپت زیر ریپو را با ۱۴ فایل fixture می‌سازد. ۱۰ تا از آن‌ها مشکل کاشته‌شده دارند و ۴ تا تمیزند. فایل‌های تمیز به اندازه فایل‌های خراب مهم‌اند، چون ریویوکننده‌ای که همه چیز را flag کند به هیچ دردی نمی‌خورد. در fixtures/labels.json هم دقیقاً نوشته شده هر فایل چه مشکلی دارد.

اسکریپت را در فولدری اجرا کن که کدهایت را آنجا نگه می‌داری:

bash
#!/usr/bin/env bash
# Creates the sentinel repo with labelled fixtures. Safe to read before running:
# it only writes files under ./sentinel and runs git init.
set -euo pipefail
mkdir -p sentinel/fixtures/k8s sentinel/fixtures/js && cd sentinel
git init -q

cat > fixtures/k8s/k01-privileged.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata: { name: debug-tools }
spec:
  containers:
    - name: tools
      image: registry.example.com/tools:1.4.2
      securityContext: { privileged: true }
EOF

cat > fixtures/k8s/k02-hostpath.yaml <<'EOF'
apiVersion: apps/v1
kind: DaemonSet
metadata: { name: log-shipper }
spec:
  selector: { matchLabels: { app: log-shipper } }
  template:
    metadata: { labels: { app: log-shipper } }
    spec:
      containers:
        - name: shipper
          image: registry.example.com/shipper:2.0.1
          volumeMounts: [{ name: root, mountPath: /host }]
      volumes:
        - name: root
          hostPath: { path: / }
EOF

cat > fixtures/k8s/k03-secret-literal.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata: { name: billing }
spec:
  replicas: 2
  selector: { matchLabels: { app: billing } }
  template:
    metadata: { labels: { app: billing } }
    spec:
      containers:
        - name: api
          image: registry.example.com/billing:3.2.0
          env:
            - name: STRIPE_SECRET_KEY
              value: sk_live_FAKE_FOR_TRAINING_51Hx9
EOF

cat > fixtures/k8s/k04-latest-root.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
  replicas: 3
  selector: { matchLabels: { app: web } }
  template:
    metadata: { labels: { app: web } }
    spec:
      containers:
        - name: web
          image: nginx:latest
          securityContext: { runAsUser: 0 }
EOF

cat > fixtures/k8s/k05-clean-deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata: { name: catalog }
spec:
  replicas: 2
  selector: { matchLabels: { app: catalog } }
  template:
    metadata: { labels: { app: catalog } }
    spec:
      automountServiceAccountToken: false
      securityContext: { runAsNonRoot: true, seccompProfile: { type: RuntimeDefault } }
      containers:
        - name: api
          image: registry.example.com/catalog@sha256:3f1c2b9d8e7a6f5e4d3c2b1a09f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c
          securityContext:
            runAsUser: 10001
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities: { drop: [ALL] }
          resources:
            requests: { cpu: 100m, memory: 128Mi }
            limits: { memory: 256Mi }
          env:
            - name: DB_PASSWORD
              valueFrom: { secretKeyRef: { name: catalog-db, key: password } }
EOF

cat > fixtures/k8s/k06-rbac-wildcard.yaml <<'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata: { name: ci-deployer }
rules:
  - apiGroups: ["*"]
    resources: ["*"]
    verbs: ["*"]
EOF

cat > fixtures/k8s/k07-hostnetwork.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata: { name: metrics-agent }
spec:
  hostNetwork: true
  hostPID: true
  containers:
    - name: agent
      image: registry.example.com/metrics-agent:0.9.3
EOF

cat > fixtures/k8s/k08-clean-netpol.yaml <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny, namespace: billing }
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]
EOF

cat > fixtures/js/j01-cmd-injection.js <<'EOF'
const { exec } = require("node:child_process");
module.exports = (req, res) => {
  exec(`ping -c 1 ${req.query.host}`, (err, out) => res.send(out));
};
EOF

cat > fixtures/js/j02-eval.js <<'EOF'
module.exports = (req, res) => {
  const result = eval(req.body.expression);
  res.json({ result });
};
EOF

cat > fixtures/js/j03-ssrf.js <<'EOF'
module.exports = async (req, res) => {
  const r = await fetch(req.query.url);
  res.send(await r.text());
};
EOF

cat > fixtures/js/j04-clean-execfile.js <<'EOF'
const { execFile } = require("node:child_process");
const HOSTS = new Set(["db.internal", "cache.internal"]);
module.exports = (req, res) => {
  const host = String(req.query.host);
  if (!HOSTS.has(host)) return res.status(400).send("unknown host");
  execFile("ping", ["-c", "1", host], (err, out) => res.send(out));
};
EOF

cat > fixtures/js/j05-jwt-decode.js <<'EOF'
const jwt = require("jsonwebtoken");
module.exports = (req, res, next) => {
  const token = req.headers.authorization?.split(" ")[1];
  const claims = jwt.decode(token);
  if (claims?.role === "admin") return next();
  res.status(403).end();
};
EOF

cat > fixtures/js/j06-clean-path.js <<'EOF'
const path = require("node:path");
const fs = require("node:fs/promises");
const BASE = path.resolve("/srv/reports");
module.exports = async (req, res) => {
  const file = path.resolve(BASE, req.params.name);
  if (!file.startsWith(BASE + path.sep)) return res.status(400).end();
  res.send(await fs.readFile(file, "utf8"));
};
EOF

cat > fixtures/labels.json <<'EOF'
{
  "k8s/k01-privileged.yaml":     ["privileged-container"],
  "k8s/k02-hostpath.yaml":       ["hostpath-mount"],
  "k8s/k03-secret-literal.yaml": ["secret-in-plain-env"],
  "k8s/k04-latest-root.yaml":    ["mutable-image-tag", "runs-as-root"],
  "k8s/k05-clean-deployment.yaml": [],
  "k8s/k06-rbac-wildcard.yaml":  ["rbac-wildcard"],
  "k8s/k07-hostnetwork.yaml":    ["host-network", "host-pid"],
  "k8s/k08-clean-netpol.yaml":   [],
  "js/j01-cmd-injection.js":     ["command-injection"],
  "js/j02-eval.js":              ["code-injection-eval"],
  "js/j03-ssrf.js":              ["ssrf"],
  "js/j04-clean-execfile.js":    [],
  "js/j05-jwt-decode.js":        ["jwt-not-verified"],
  "js/j06-clean-path.js":        []
}
EOF

git add -A && git commit -qm "Fixtures and labels" && echo "sentinel ready: $(pwd)"

سه نکته درباره این برچسب‌ها:

  • هر برچسب مشکل اصلی آن فایل است، نه هر ایراد ریزی که بشود گرفت. مثلاً k01 نه resource limit دارد و نه digest را pin کرده است. اگر ریویوکننده‌ای این‌ها را گزارش کند اشتباه نکرده. در ماژول ۳ تصمیم می‌گیری با یافته‌های «درست ولی بیرون از برچسب‌ها» چه کنی، و خود این تصمیم بخشی از درس است.
  • سکرت داخل k03 فیک است و روی خودش هم نوشته که فیک است. هیچ وقت credential واقعی در fixture نگذار، حتی اگر revoke شده باشد.
  • نگذار Codex فولدر fixtures/ را ویرایش کند. برچسب‌ها داده مرجع تو هستند. اگر مدل بتواند عوضشان کند، هر evalی را می‌تواند پاس کند. ماژول ۱ این قانون را در AGENTS.md می‌نویسد و ماژول ۲ کاری می‌کند که واقعاً رعایت شود.

#مثال کامل: اولین کار اندازه‌گیری‌شده

حالا یک خط پایه دوم می‌گیری که ماژول ۱ باید بهترش کند. ریپو را در VS Code با پنل Codex باز کن، یا داخل فولدر sentinel دستور codex را بزن. وقتی پرسید، به فولدر اعتماد کن، چون همین الان خودت آن را ساختی. دقیقاً همین پرامپت را بده و هیچ چیز دیگری نگو:

text
Create a TypeScript CLI in src/ that reads every file under fixtures/k8s and prints,
for each one, a list of security problems found with simple rules (no AI).
Use Node 22, no framework. Add npm scripts "build" and "scan". Don't touch fixtures/.

یعنی از Codex می‌خواهی در src/ یک CLI با TypeScript بسازد که همه فایل‌های fixtures/k8s را بخواند و برای هر کدام، با قانون‌های ساده و بدون AI، فهرست مشکلات امنیتی را چاپ کند. باید با Node 22 و بدون فریم‌ورک باشد، دو اسکریپت npm به اسم build و scan داشته باشد و به fixtures/ دست نزند.

در طول کار این‌ها را بشمار:

  • دخالت‌ها: هر بار که مجبور شدی چیزی غیر از تأیید یک درخواست تایپ کنی.
  • تأییدها: هر درخواست دسترسی که جوابش را دادی.
  • دقیقه‌ها: از وقتی پرامپت را دادی تا وقتی npm run scan درست کار کند.
  • امتیاز: خروجی npm run scan چند تا از ۸ فایل k8s را درست برچسب می‌زند. درست یعنی مشکل‌های درست را پیدا کند و روی فایل‌های تمیز هیچ چیزی نگوید.

عددها را پایین‌تر در بخش تمرین بنویس. هیچ چیزی را دستی درست نکن و به Codex راهنمایی هم نده. اینجا فقط عدد مهم است و کیفیت کد فعلاً اهمیتی ندارد. ماژول ۱ دستورالعمل‌های همین کار را بازنویسی می‌کند و دوباره اندازه می‌گیرد.

#تمرین

عددهای اولین کار: دخالت‌ها · تأییدها · دقیقه‌ها · فایل‌هایی که درست برچسب خوردند

کار وقتی تمام است که امتیاز audit ده باشد یا دلیلش را نوشته باشی، ریپو و برچسب‌ها ساخته شده باشند و عددهای اولین کار ثبت شده باشند.

#خودت را بسنج

قبل از باز کردن هر سؤال، جوابش را در ذهنت یا روی کاغذ بده.

۱. یک همکار در بلاگش نوشته `[profiles.ci]` را به config.toml اضافه کن و بعد `codex --full-auto` را بزن. ایراد این توصیه چیست؟

هر دو قدیمی شده‌اند. از 0.134.0 دیگر جدول‌های [profiles.*] کار نمی‌کنند. حالا هر پروفایل فایل خودش را دارد، یعنی ~/.codex/ci.config.toml، و با codex --profile ci اجرا می‌شود. فلگ --full-auto هم دیگر پشتیبانی نمی‌شود. به جایش --sandbox workspace-write را بزن، یا --approve-for-me را تا درخواست‌های تأیید برای ریویو خودکار فرستاده شوند. جدول پروفایل را هم codex doctor (با یک خط ⚠ config) می‌گرفت و هم codex --strict-config.

۲. یک ریپو را کلون کرده‌ای تا یک pull request را ریویو کنی. Codex می‌پرسد به فولدر اعتماد داری یا نه. اگر بله بگویی چه چیزی عوض می‌شود؟

اگر بله بگویی، Codex فولدر .codex/ ریپو را لود می‌کند، یعنی کانفیگ، hookها و ruleهای دستورهایش را. کانفیگ می‌تواند برای همان پروژه مدل، حالت سندباکس و approval policy را عوض کند. hookها هم اسکریپت‌هایی‌اند که روی رویدادهای Codex اجرا می‌شوند. اگر نه بگویی همه این‌ها کنار گذاشته می‌شوند و ممکن است Codex فقط‌خواندنی شروع شود. برای ریپویی که داری ریویو می‌کنی نه بگو و اول خودت .codex/ و AGENTS.md را بخوان.

۳. چه فرقی می‌کند توکن ChatGPT تو در `auth.json` باشد یا در Keychain؟

فایل auth.json یک فایل plaintext در فولدر home توست و هر پروسه‌ای که با یوزر تو اجرا شود می‌تواند آن را بخواند. مثلاً یک اسکریپت postinstall در npm، یا دستوری که یک ایجنت را فریب داده‌اند تا اجرایش کند. با cli_auth_credentials_store = "keyring" توکن در Keychain مک نگه داشته می‌شود و با خواندن ساده یک فایل نمی‌شود به آن رسید.

۴. سندباکس پیش‌فرض workspace-write است. با این حساب Codex می‌تواند `~/.aws/credentials` را بخواند؟

معمولاً بله. حالت workspace-write فقط محدود می‌کند Codex کجا می‌تواند بنویسد، یعنی workspace و فولدرهای temp. حتی داخل workspace هم .git، .codex، .agents و .aws فقط‌خواندنی می‌مانند. ولی جلوی خواندن بقیه دیسک را نمی‌گیرد. در ژوئیه ۲۰۲۶ یک حمله دقیقاً از همین استفاده کرد و با یک دستور در AGENTS.md کاری کرد که codex exec فایل ~/.aws/credentials را بخواند. راه‌حلش قانون‌های deny برای هر مسیر است (ماژول ۲)، به‌علاوه اینکه credential بلندمدت روی ماشین نگه نداری.

۵. چرا `sentinel` چند fixture تمیز با برچسب خالی دارد؟

برای اینکه false positiveها را بسنجی. ریویوکننده‌ای که همه فایل‌ها را flag کند، روی همه فایل‌های خراب جواب «درست» داده ولی به هیچ دردی نمی‌خورد. فایل‌های تمیز نشان می‌دهند می‌تواند بگوید «اینجا چیزی نیست» یا نه. در pull requestهای واقعی هم یک ریویوکننده بیشتر وقت‌ها با همین حالت روبه‌روست.

۶. خود Codex با پلن تو کار می‌کند. پس کی API key لازم داری و چه چیزی جلوی خرجش را می‌گیرد؟

فقط وقتی کد خودت OpenAI API را صدا می‌زند، یعنی از ماژول ۵ به بعد. مصرف API جدا از ChatGPT Plus یا Pro حساب می‌شود. سقفش همان بودجه ماهانه پروژه sentinel است. خود key هم فقط به همان پروژه محدود شده تا اگر لو رفت، نشود با آن جای دیگری خرج کرد.

#چه یاد گرفتم

#کجا به کار بردم

#مرور

#منابع

به همین ترتیب بخوان. همه در ۱۱ نوامبر ۲۰۲۶ چک شده‌اند.

  1. مستندات Codex، بخش CLI: نصب، آپدیت و استفاده پایه. https://learn.chatgpt.com/docs/codex/cli.md
  2. مستندات Codex، بخش Agent approvals and security: presetها، شبکه، مسیرهای محافظت‌شده و توصیه‌های امنیتی خود OpenAI. همه‌اش را بخوان. https://learn.chatgpt.com/docs/agent-approvals-security.md
  3. مستندات Codex، بخش Advanced config: لایه‌ها، فایل‌های پروفایل، چیزهایی که کانفیگ پروژه نمی‌تواند ست کند و telemetry. https://learn.chatgpt.com/docs/config-file/config-advanced.md
  4. مستندات Codex، بخش Config reference: قبل از اینکه به یک پست بلاگ اعتماد کنی، هر کلید را اینجا چک کن. https://learn.chatgpt.com/docs/config-file/config-reference.md
  5. مستندات Codex، بخش Authentication: ذخیره در keyring، device auth، روش‌های لاگین. https://learn.chatgpt.com/docs/auth.md
  6. مستندات Codex، بخش Models: کدام مدل برای چه کاری است. https://learn.chatgpt.com/docs/models.md
  7. مستندات Codex، بخش Pricing: سقف پلن برای هر مدل. عددها عوض می‌شوند، پس قبل از ماژول ۳ دوباره نگاهش کن. https://learn.chatgpt.com/docs/pricing.md
  8. مستندات Codex، بخش IDE extension. https://learn.chatgpt.com/docs/codex/ide.md
  9. هشدار امنیتی OpenAI درباره CVE-2026-19592 (فرار از طریق core.fsmonitor). کوتاه است و نمونه روشنی از همین الگو. https://openai.com/security/advisories/cve-2026-19592/
  10. گزارش Check Point Research درباره command injection در Codex CLI (CVE-2025-61260). نشان می‌دهد چرا Codex حالا قبل از لود کردن کانفیگ پروژه می‌پرسد به آن اعتماد داری یا نه. https://research.checkpoint.com/2025/openai-codex-cli-command-injection-vulnerability
  11. مستندات changelog Codex. اول هر ماژول نگاهی سریع به تغییرات ماه گذشته بینداز. https://learn.chatgpt.com/docs/changelog