exa-policy-guardrails

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Exa Policy & Guardrails

Exa 策略与防护机制

Overview

概述

Automated policy enforcement and guardrails for Exa integrations.
为Exa集成提供自动化策略执行与防护机制。

Prerequisites

前置条件

  • ESLint configured in project
  • Pre-commit hooks infrastructure
  • CI/CD pipeline with policy checks
  • TypeScript for type enforcement
  • 项目中已配置ESLint
  • 具备提交前钩子基础设施
  • 带有策略检查的CI/CD流水线
  • 用于类型校验的TypeScript

ESLint Rules

ESLint规则

Custom Exa Plugin

自定义Exa插件

javascript
// eslint-plugin-exa/rules/no-hardcoded-keys.js
module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'Disallow hardcoded Exa API keys',
    },
    fixable: 'code',
  },
  create(context) {
    return {
      Literal(node) {
        if (typeof node.value === 'string') {
          if (node.value.match(/^sk_(live|test)_[a-zA-Z0-9]{24,}/)) {
            context.report({
              node,
              message: 'Hardcoded Exa API key detected',
            });
          }
        }
      },
    };
  },
};
javascript
// eslint-plugin-exa/rules/no-hardcoded-keys.js
module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'Disallow hardcoded Exa API keys',
    },
    fixable: 'code',
  },
  create(context) {
    return {
      Literal(node) {
        if (typeof node.value === 'string') {
          if (node.value.match(/^sk_(live|test)_[a-zA-Z0-9]{24,}/)) {
            context.report({
              node,
              message: 'Hardcoded Exa API key detected',
            });
          }
        }
      },
    };
  },
};

ESLint Configuration

ESLint配置

javascript
// .eslintrc.js
module.exports = {
  plugins: ['exa'],
  rules: {
    'exa/no-hardcoded-keys': 'error',
    'exa/require-error-handling': 'warn',
    'exa/use-typed-client': 'warn',
  },
};
javascript
// .eslintrc.js
module.exports = {
  plugins: ['exa'],
  rules: {
    'exa/no-hardcoded-keys': 'error',
    'exa/require-error-handling': 'warn',
    'exa/use-typed-client': 'warn',
  },
};

Pre-Commit Hooks

提交前钩子

yaml
undefined
yaml
undefined

.pre-commit-config.yaml

.pre-commit-config.yaml

repos:
  • repo: local hooks:
    • id: exa-secrets-check name: Check for Exa secrets entry: bash -c 'git diff --cached --name-only | xargs grep -l "sk_live_" && exit 1 || exit 0' language: system pass_filenames: false
    • id: exa-config-validate name: Validate Exa configuration entry: node scripts/validate-exa-config.js language: node files: '.exa.json$'
undefined
repos:
  • repo: local hooks:
    • id: exa-secrets-check name: Check for Exa secrets entry: bash -c 'git diff --cached --name-only | xargs grep -l "sk_live_" && exit 1 || exit 0' language: system pass_filenames: false
    • id: exa-config-validate name: Validate Exa configuration entry: node scripts/validate-exa-config.js language: node files: '.exa.json$'
undefined

TypeScript Strict Patterns

TypeScript严格模式规范

typescript
// Enforce typed configuration
interface ExaStrictConfig {
  apiKey: string;  // Required
  environment: 'development' | 'staging' | 'production';  // Enum
  timeout: number;  // Required number, not optional
  retries: number;
}

// Disallow any in Exa code
// @ts-expect-error - Using any is forbidden
const client = new Client({ apiKey: any });

// Prefer this
const client = new ExaClient(config satisfies ExaStrictConfig);
typescript
// Enforce typed configuration
interface ExaStrictConfig {
  apiKey: string;  // Required
  environment: 'development' | 'staging' | 'production';  // Enum
  timeout: number;  // Required number, not optional
  retries: number;
}

// Disallow any in Exa code
// @ts-expect-error - Using any is forbidden
const client = new Client({ apiKey: any });

// Prefer this
const client = new ExaClient(config satisfies ExaStrictConfig);

Architecture Decision Records

架构决策记录

ADR Template

ADR模板

markdown
undefined
markdown
undefined

ADR-001: Exa Client Initialization

ADR-001: Exa Client Initialization

Status

Status

Accepted
Accepted

Context

Context

We need to decide how to initialize the Exa client across our application.
We need to decide how to initialize the Exa client across our application.

Decision

Decision

We will use the singleton pattern with lazy initialization.
We will use the singleton pattern with lazy initialization.

Consequences

Consequences

  • Pro: Single client instance, connection reuse
  • Pro: Easy to mock in tests
  • Con: Global state requires careful lifecycle management
  • Pro: Single client instance, connection reuse
  • Pro: Easy to mock in tests
  • Con: Global state requires careful lifecycle management

Enforcement

Enforcement

  • ESLint rule: exa/use-singleton-client
  • CI check: grep for "new ExaClient(" outside allowed files
undefined
  • ESLint rule: exa/use-singleton-client
  • CI check: grep for "new ExaClient(" outside allowed files
undefined

Policy-as-Code (OPA)

即代码策略(OPA)

rego
undefined
rego
undefined

exa-policy.rego

exa-policy.rego

package exa
package exa

Deny production API keys in non-production environments

Deny production API keys in non-production environments

deny[msg] { input.environment != "production" startswith(input.apiKey, "sk_live_") msg := "Production API keys not allowed in non-production environment" }
deny[msg] { input.environment != "production" startswith(input.apiKey, "sk_live_") msg := "Production API keys not allowed in non-production environment" }

Require minimum timeout

Require minimum timeout

deny[msg] { input.timeout < 10000 msg := sprintf("Timeout too low: %d < 10000ms minimum", [input.timeout]) }
deny[msg] { input.timeout < 10000 msg := sprintf("Timeout too low: %d < 10000ms minimum", [input.timeout]) }

Require retry configuration

Require retry configuration

deny[msg] { not input.retries msg := "Retry configuration is required" }
undefined
deny[msg] { not input.retries msg := "Retry configuration is required" }
undefined

CI Policy Checks

CI策略检查

yaml
undefined
yaml
undefined

.github/workflows/exa-policy.yml

.github/workflows/exa-policy.yml

name: Exa Policy Check
on: [push, pull_request]
jobs: policy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
  - name: Check for hardcoded secrets
    run: |
      if grep -rE "sk_(live|test)_[a-zA-Z0-9]{24,}" --include="*.ts" --include="*.js" .; then
        echo "ERROR: Hardcoded Exa keys found"
        exit 1
      fi

  - name: Validate configuration schema
    run: |
      npx ajv validate -s exa-config.schema.json -d config/exa/*.json

  - name: Run ESLint Exa rules
    run: npx eslint --plugin exa --rule 'exa/no-hardcoded-keys: error' src/
undefined
name: Exa Policy Check
on: [push, pull_request]
jobs: policy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
  - name: Check for hardcoded secrets
    run: |
      if grep -rE "sk_(live|test)_[a-zA-Z0-9]{24,}" --include="*.ts" --include="*.js" .; then
        echo "ERROR: Hardcoded Exa keys found"
        exit 1
      fi

  - name: Validate configuration schema
    run: |
      npx ajv validate -s exa-config.schema.json -d config/exa/*.json

  - name: Run ESLint Exa rules
    run: npx eslint --plugin exa --rule 'exa/no-hardcoded-keys: error' src/
undefined

Runtime Guardrails

运行时防护机制

typescript
// Prevent dangerous operations in production
const BLOCKED_IN_PROD = ['deleteAll', 'resetData', 'migrateDown'];

function guardExaOperation(operation: string): void {
  const isProd = process.env.NODE_ENV === 'production';

  if (isProd && BLOCKED_IN_PROD.includes(operation)) {
    throw new Error(`Operation '${operation}' blocked in production`);
  }
}

// Rate limit protection
function guardRateLimits(requestsInWindow: number): void {
  const limit = parseInt(process.env.EXA_RATE_LIMIT || '100');

  if (requestsInWindow > limit * 0.9) {
    console.warn('Approaching Exa rate limit');
  }

  if (requestsInWindow >= limit) {
    throw new Error('Exa rate limit exceeded - request blocked');
  }
}
typescript
// Prevent dangerous operations in production
const BLOCKED_IN_PROD = ['deleteAll', 'resetData', 'migrateDown'];

function guardExaOperation(operation: string): void {
  const isProd = process.env.NODE_ENV === 'production';

  if (isProd && BLOCKED_IN_PROD.includes(operation)) {
    throw new Error(`Operation '${operation}' blocked in production`);
  }
}

// Rate limit protection
function guardRateLimits(requestsInWindow: number): void {
  const limit = parseInt(process.env.EXA_RATE_LIMIT || '100');

  if (requestsInWindow > limit * 0.9) {
    console.warn('Approaching Exa rate limit');
  }

  if (requestsInWindow >= limit) {
    throw new Error('Exa rate limit exceeded - request blocked');
  }
}

Instructions

操作步骤

Step 1: Create ESLint Rules

步骤1:创建ESLint规则

Implement custom lint rules for Exa patterns.
实现针对Exa模式的自定义代码检查规则。

Step 2: Configure Pre-Commit Hooks

步骤2:配置提交前钩子

Set up hooks to catch issues before commit.
设置钩子以在提交前捕获问题。

Step 3: Add CI Policy Checks

步骤3:添加CI策略检查

Implement policy-as-code in CI pipeline.
在CI流水线中实现即代码策略。

Step 4: Enable Runtime Guardrails

步骤4:启用运行时防护机制

Add production safeguards for dangerous operations.
为危险操作添加生产环境防护措施。

Output

输出结果

  • ESLint plugin with Exa rules
  • Pre-commit hooks blocking secrets
  • CI policy checks passing
  • Runtime guardrails active
  • 带有Exa规则的ESLint插件
  • 阻止密钥提交的提交前钩子
  • 已通过的CI策略检查
  • 已激活的运行时防护机制

Error Handling

错误处理

IssueCauseSolution
ESLint rule not firingWrong configCheck plugin registration
Pre-commit skipped--no-verifyEnforce in CI
Policy false positiveRegex too broadNarrow pattern match
Guardrail triggeredActual issueFix or whitelist
问题原因解决方案
ESLint规则未触发配置错误检查插件注册情况
提交前钩子被跳过使用了--no-verify参数在CI中强制执行
策略误报正则表达式范围过宽缩小匹配模式
防护机制被触发存在实际问题修复或加入白名单

Examples

示例

Quick ESLint Check

快速ESLint检查

bash
npx eslint --plugin exa --rule 'exa/no-hardcoded-keys: error' src/
bash
npx eslint --plugin exa --rule 'exa/no-hardcoded-keys: error' src/

Resources

参考资源

Next Steps

下一步

For architecture blueprints, see
exa-architecture-variants
.
如需架构蓝图,请查看
exa-architecture-variants