Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions docs/specs/pipeline-recipe-builder-technical-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ export interface PipelineToolAdapter<Input = string, Output = string> {
warnings: readonly string[]
defaultOptions: Record<string, unknown>
publicOptionKeys: readonly string[]
persistentOptionKeys?: readonly string[]
persistentOptionReview?: Record<string, string>
validateOptions(options: Record<string, unknown>): AdapterValidationResult
run(input: Input, options: Record<string, unknown>): Promise<AdapterRunResult<Output>> | AdapterRunResult<Output>
}
Expand Down Expand Up @@ -173,7 +175,19 @@ Adapter rules:
- `warnings` must describe persistent adapter-level caveats shown or available to UI surfaces.
- Adapters must return structured warnings instead of throwing for expected user errors.
- Adapters must have unit tests covering success and failure paths.
- `publicOptionKeys` controls which options are allowed into shared recipe URLs.
- `publicOptionKeys` controls which options are editable or visible in the Pipeline Builder UI.
- `persistentOptionKeys` controls which reviewed options are allowed into saved recipes, exported recipe JSON, and shared recipe URLs. If omitted, it defaults to `publicOptionKeys` for backwards compatibility.
- `persistentOptionReview` must document any persisted option whose key looks sensitive even when the persisted value is a safe scalar, such as `urlSafe` or a boolean scrub-rule toggle.

Option persistence taxonomy:

| Persistence | Meaning | Examples |
|-------------|---------|----------|
| Safe | Reviewed scalar configuration that does not carry user-authored payload content. | `mode`, `indent`, `format`, `operation`, bounded numeric limits, boolean toggles. |
| Sensitive | User-authored text, structured data, or values likely to contain private contract details. These are excluded by default. | `schema`, regex `pattern`, headers, bodies, URLs, payloads, examples, defaults, constants. |
| Reviewed exception | A safe scalar whose key contains a suspicious substring and therefore needs an explicit reason. | `urlSafe`, boolean log scrubber toggles such as `apiKeys` or `bearerTokens`. |

Guard tests must prevent new suspicious persistent option keys containing `token`, `secret`, `key`, `url`, `header`, `body`, `payload`, `input`, `output`, `example`, `default`, `const`, `query`, `endpoint`, `host`, `schema`, or `pattern` unless `persistentOptionReview` explains why the value is safe to persist.

## 6. Adapter Sets

Expand Down Expand Up @@ -240,7 +254,8 @@ Recipe URL sharing should encode recipe structure only:
Rules:

- Do not include runtime input by default.
- Do not include unknown adapter options; share URLs keep only keys declared in each adapter's `publicOptionKeys`.
- Do not include unknown adapter options; share URLs keep only keys declared in each adapter's `persistentOptionKeys` or the fallback persistent key set.
- Do not include user-authored schemas, regex patterns, examples, defaults, URLs, headers, bodies, or payload-like options by default.
- If a user explicitly chooses to include sample input, show a privacy warning.
- Enforce a URL length budget and fall back to export JSON when the recipe is too large.
- Reuse base64url behavior consistent with `tool-handoff`.
Expand Down Expand Up @@ -311,6 +326,7 @@ Failure states must be explicit:
- Runtime payloads stay in React state or browser storage only when the user explicitly saves a recipe with sample input.
- Share URLs must not include payloads by default.
- Analytics must never include recipe input, step output, secrets, or raw options that could contain data.
- Saved, exported, and shared recipes persist reviewed safe options only. User-authored schemas, regex patterns, examples, defaults, URLs, headers, bodies, and payload-like options stay out of the structure-only recipe boundary by default.
- Export should be user-triggered only.
- Imported recipes should never auto-run.

Expand Down Expand Up @@ -412,7 +428,7 @@ Required gates:
Continue workbench hardening after the public MVP stays green:

1. Run browser QA across the built-in templates in all supported locales.
2. Add more deterministic adapters only after each has explicit validation and `publicOptionKeys`.
2. Add more deterministic adapters only after each has explicit validation, `publicOptionKeys`, and reviewed `persistentOptionKeys` for save/export/share behavior.
3. Add direct "Send to Pipeline" actions from selected mature tool pages.
4. Evaluate worker-based execution only if large recipes make main-thread execution a real problem.
5. Keep branching/merge graph execution out of scope until a separate graph executor design exists.
5 changes: 3 additions & 2 deletions src/core/i18n/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -2849,7 +2849,7 @@
"privacy_preview_confirm_share": "Struktur-URL kopieren",
"privacy_preview_cancel": "Abbrechen",
"privacy_scope_recipe_metadata": "Rezeptname und Beschreibung",
"privacy_scope_steps_options": "Schrittreihenfolge, Tool-IDs, Labels und öffentliche Optionen",
"privacy_scope_steps_options": "Schrittreihenfolge, Tool-IDs, Labels und geprüfte dauerhafte Optionen",
"privacy_scope_settings": "Sicherheitseinstellungen wie Stop-on-Error und lokale Byte-Limits",
"privacy_scope_runtime_input": "Ursprüngliche Laufzeiteingabe",
"privacy_scope_outputs": "Endausgabe und Zwischenoutputs",
Expand Down Expand Up @@ -2889,7 +2889,8 @@
"usage_guide_composition_title": "Kompositionsregeln",
"usage_guide_composition_body": "Tools koennen wiederholt werden. Die Validierung prueft Adapterversionen und Optionen, waehrend Kompatibilitaetshinweise Uebergaben wie Textausgabe in JSON-Eingabe markieren.",
"usage_guide_privacy_title": "Datenschutzgrenze",
"usage_guide_privacy_body": "Save, Export JSON und Share URL behalten nur Struktur und oeffentliche Optionen. Laufzeiteingaben, Ausgaben, Logs, Dateien und konstante Eingaben werden entfernt."
"usage_guide_privacy_body": "Save, Export JSON und Share URL behalten nur Struktur und gepruefte dauerhafte Optionen. Laufzeiteingaben, Ausgaben, Logs, Dateien, konstante Eingaben, Schemas und Regex-Muster werden entfernt.",
"privacy_scope_user_authored_options": "Benutzerdefinierte Schemas, Regex-Muster, Beispiele, Standardwerte, URLs, Header, Bodies und Nutzdaten"
},
"saml_decoder": {
"title": "SAML-Dekoder",
Expand Down
7 changes: 4 additions & 3 deletions src/core/i18n/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2829,7 +2829,7 @@
"usage_guide_composition_title": "Composition rules",
"usage_guide_composition_body": "Tools can repeat. Validation checks adapter versions and options, while compatibility hints flag handoffs such as text output into JSON input.",
"usage_guide_privacy_title": "Privacy boundary",
"usage_guide_privacy_body": "Save, Export JSON, and Share URL keep structure and public options only. Runtime input, outputs, logs, files, and Constant input are stripped.",
"usage_guide_privacy_body": "Save, Export JSON, and Share URL keep structure and reviewed persistent options only. Runtime input, outputs, logs, files, Constant input, schemas, and regex patterns are stripped.",
"no_options": "This adapter has no configurable public options.",
"option_mode": "Mode",
"option_indent": "Indent",
Expand Down Expand Up @@ -2903,13 +2903,14 @@
"privacy_preview_confirm_share": "Copy structure-only URL",
"privacy_preview_cancel": "Cancel",
"privacy_scope_recipe_metadata": "Recipe name and description",
"privacy_scope_steps_options": "Step order, tool IDs, labels, and public options",
"privacy_scope_steps_options": "Step order, tool IDs, labels, and reviewed persistent options",
"privacy_scope_settings": "Safety settings such as stop-on-error and local byte limits",
"privacy_scope_runtime_input": "Initial runtime input",
"privacy_scope_outputs": "Final output and intermediate step outputs",
"privacy_scope_logs": "Run logs, diagnostics, and errors",
"privacy_scope_files": "Uploaded file contents",
"privacy_scope_constants": "Constant step inputs, tokens, keys, and payloads"
"privacy_scope_constants": "Constant step inputs, tokens, keys, and payloads",
"privacy_scope_user_authored_options": "User-authored schemas, regex patterns, examples, defaults, URLs, headers, bodies, and payloads"
},
"saml_decoder": {
"title": "SAML Decoder",
Expand Down
5 changes: 3 additions & 2 deletions src/core/i18n/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2849,7 +2849,7 @@
"privacy_preview_confirm_share": "Copier l’URL structure seule",
"privacy_preview_cancel": "Annuler",
"privacy_scope_recipe_metadata": "Nom et description de la recette",
"privacy_scope_steps_options": "Ordre des étapes, ID des outils, libellés et options publiques",
"privacy_scope_steps_options": "Ordre des étapes, ID des outils, libellés et options persistantes vérifiées",
"privacy_scope_settings": "Paramètres de sécurité comme l'arrêt sur erreur et les limites locales en octets",
"privacy_scope_runtime_input": "Entrée d'exécution initiale",
"privacy_scope_outputs": "Sortie finale et sorties intermédiaires",
Expand Down Expand Up @@ -2889,7 +2889,8 @@
"usage_guide_composition_title": "Regles de composition",
"usage_guide_composition_body": "Les outils peuvent se repeter. La validation verifie versions d'adaptateur et options, tandis que les indications de compatibilite signalent les passages comme une sortie texte vers une entree JSON.",
"usage_guide_privacy_title": "Limite de confidentialite",
"usage_guide_privacy_body": "Save, Export JSON et Share URL conservent uniquement la structure et les options publiques. Entrees d'execution, sorties, logs, fichiers et entrees constantes sont retires."
"usage_guide_privacy_body": "Save, Export JSON et Share URL conservent uniquement la structure et les options persistantes verifiees. Entrees d'execution, sorties, logs, fichiers, entrees constantes, schemas et motifs regex sont retires.",
"privacy_scope_user_authored_options": "Schémas, motifs regex, exemples, valeurs par défaut, URL, en-têtes, corps et charges utiles rédigés par l'utilisateur"
},
"saml_decoder": {
"title": "Décodeur SAML",
Expand Down
5 changes: 3 additions & 2 deletions src/core/i18n/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -2849,7 +2849,7 @@
"privacy_preview_confirm_share": "構造のみの URL をコピー",
"privacy_preview_cancel": "キャンセル",
"privacy_scope_recipe_metadata": "レシピ名と説明",
"privacy_scope_steps_options": "ステップ順序、ツール ID、ラベル、公開オプション",
"privacy_scope_steps_options": "ステップ順序、ツール ID、ラベル、レビュー済みの永続化オプション",
"privacy_scope_settings": "エラー時停止やローカルバイト上限などの安全設定",
"privacy_scope_runtime_input": "初期実行入力",
"privacy_scope_outputs": "最終出力と中間ステップ出力",
Expand Down Expand Up @@ -2889,7 +2889,8 @@
"usage_guide_composition_title": "組み合わせルール",
"usage_guide_composition_body": "ツールは繰り返せます。検証はアダプタバージョンとオプションを確認し、互換性ヒントはテキスト出力から JSON 入力のような受け渡しを示します。",
"usage_guide_privacy_title": "プライバシー境界",
"usage_guide_privacy_body": "保存、JSON 書き出し、共有 URL は構造と公開オプションのみ保持します。実行時入力、出力、ログ、ファイル、固定入力は削除されます。"
"usage_guide_privacy_body": "保存、JSON 書き出し、共有 URL は構造とレビュー済みの永続化オプションのみ保持します。実行時入力、出力、ログ、ファイル、固定入力、スキーマ、正規表現パターンは削除されます。",
"privacy_scope_user_authored_options": "ユーザー作成のスキーマ、正規表現パターン、例、デフォルト、URL、ヘッダー、本文、ペイロード"
},
"saml_decoder": {
"title": "SAML デコーダー",
Expand Down
5 changes: 3 additions & 2 deletions src/core/i18n/translations/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -2849,7 +2849,7 @@
"privacy_preview_confirm_share": "구조 전용 URL 복사",
"privacy_preview_cancel": "취소",
"privacy_scope_recipe_metadata": "레시피 이름과 설명",
"privacy_scope_steps_options": "단계 순서, 도구 ID, 라벨, 공개 옵션",
"privacy_scope_steps_options": "단계 순서, 도구 ID, 라벨, 검토된 영구 옵션",
"privacy_scope_settings": "오류 시 중지와 로컬 바이트 제한 같은 안전 설정",
"privacy_scope_runtime_input": "초기 실행 입력",
"privacy_scope_outputs": "최종 출력과 중간 단계 출력",
Expand Down Expand Up @@ -2889,7 +2889,8 @@
"usage_guide_composition_title": "구성 규칙",
"usage_guide_composition_body": "도구는 반복할 수 있습니다. 검증은 어댑터 버전과 옵션을 확인하며, 호환성 힌트는 text 출력을 JSON 입력으로 넘기는 경우처럼 인계를 표시합니다.",
"usage_guide_privacy_title": "개인정보 경계",
"usage_guide_privacy_body": "저장, JSON 내보내기, 공유 URL은 구조와 공개 옵션만 유지합니다. 런타임 입력, 출력, 로그, 파일, 고정 입력은 제거됩니다."
"usage_guide_privacy_body": "저장, JSON 내보내기, 공유 URL은 구조와 검토된 영구 옵션만 유지합니다. 런타임 입력, 출력, 로그, 파일, 고정 입력, 스키마, 정규식 패턴은 제거됩니다.",
"privacy_scope_user_authored_options": "사용자가 작성한 스키마, 정규식 패턴, 예시, 기본값, URL, 헤더, 본문, 페이로드"
},
"saml_decoder": {
"title": "SAML 디코더",
Expand Down
5 changes: 3 additions & 2 deletions src/core/i18n/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2849,7 +2849,7 @@
"privacy_preview_confirm_share": "复制结构分享 URL",
"privacy_preview_cancel": "取消",
"privacy_scope_recipe_metadata": "配方名称和描述",
"privacy_scope_steps_options": "步骤顺序、工具 ID、标签和公开选项",
"privacy_scope_steps_options": "步骤顺序、工具 ID、标签和已审查的持久化选项",
"privacy_scope_settings": "停止出错、 本地字节限制等安全设置",
"privacy_scope_runtime_input": "初始运行输入",
"privacy_scope_outputs": "最终输出和中间步骤输出",
Expand Down Expand Up @@ -2889,7 +2889,8 @@
"usage_guide_composition_title": "组合规则",
"usage_guide_composition_body": "工具可以重复。验证会检查适配器版本和选项,兼容性提示会标出类似文本输出接入 JSON 输入的交接。",
"usage_guide_privacy_title": "隐私边界",
"usage_guide_privacy_body": "保存、导出 JSON 和分享 URL 仅保留结构和公开选项。运行时输入、输出、日志、文件和固定输入都会被剥离。"
"usage_guide_privacy_body": "保存、导出 JSON 和分享 URL 仅保留结构和已审查的持久化选项。运行时输入、输出、日志、文件、固定输入、schema 和正则模式都会被剥离。",
"privacy_scope_user_authored_options": "用户编写的 schema、正则模式、示例、默认值、URL、标头、正文和载荷"
},
"saml_decoder": {
"title": "SAML 解码器",
Expand Down
5 changes: 3 additions & 2 deletions src/core/i18n/translations/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -2849,7 +2849,7 @@
"privacy_preview_confirm_share": "複製結構分享 URL",
"privacy_preview_cancel": "取消",
"privacy_scope_recipe_metadata": "配方名稱和描述",
"privacy_scope_steps_options": "步驟順序、工具 ID、標籤和公開選項",
"privacy_scope_steps_options": "步驟順序、工具 ID、標籤和已審查的持久化選項",
"privacy_scope_settings": "停止於錯誤、本機位元組限制等安全設定",
"privacy_scope_runtime_input": "初始執行輸入",
"privacy_scope_outputs": "最終輸出和中間步驟輸出",
Expand Down Expand Up @@ -2889,7 +2889,8 @@
"usage_guide_composition_title": "組合規則",
"usage_guide_composition_body": "工具可以重複。驗證會檢查適配器版本和選項,相容性提示會標出類似文本輸出接入 JSON 輸入的交接。",
"usage_guide_privacy_title": "隱私邊界",
"usage_guide_privacy_body": "儲存、匯出 JSON 和分享 URL 僅保留結構和公開選項。執行時輸入、輸出、日誌、檔案和固定輸入都會被移除。"
"usage_guide_privacy_body": "儲存、匯出 JSON 和分享 URL 僅保留結構和已審查的持久化選項。執行時輸入、輸出、日誌、檔案、固定輸入、schema 和正則模式都會被移除。",
"privacy_scope_user_authored_options": "使用者撰寫的 schema、正則模式、範例、預設值、URL、標頭、本文和載荷"
},
"saml_decoder": {
"title": "SAML 解碼器",
Expand Down
19 changes: 19 additions & 0 deletions src/features/pipeline/adapter-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ const base64Adapter: PipelineToolAdapter = {
urlSafe: false,
},
publicOptionKeys: ["operation", "urlSafe"],
persistentOptionReview: {
urlSafe: "Boolean output encoding toggle; it does not contain a URL value.",
},
validateOptions(options) {
const operation = stringOption(options, "operation", "encode")
if (!["encode", "decode"].includes(operation)) return fail("operation must be encode or decode.")
Expand Down Expand Up @@ -259,6 +262,14 @@ const logScrubberAdapter: PipelineToolAdapter = {
],
defaultOptions: { ...DEFAULT_SCRUB_OPTIONS },
publicOptionKeys: Object.keys(DEFAULT_SCRUB_OPTIONS),
persistentOptionReview: {
jwtTokens: "Boolean scrub-rule toggle; it does not contain token material.",
bearerTokens: "Boolean scrub-rule toggle; it does not contain token material.",
apiKeys: "Boolean scrub-rule toggle; it does not contain key material.",
awsAccessKeys: "Boolean scrub-rule toggle; it does not contain key material.",
privateKeys: "Boolean scrub-rule toggle; it does not contain key material.",
urlCredentials: "Boolean scrub-rule toggle; it does not contain a URL value.",
},
validateOptions(options) {
for (const key of Object.keys(DEFAULT_SCRUB_OPTIONS)) {
if (typeof (options[key] ?? true) !== "boolean") return fail(`${key} must be boolean.`)
Expand Down Expand Up @@ -331,6 +342,9 @@ const csvJsonAdapter: PipelineToolAdapter = {
typeInference: true,
},
publicOptionKeys: ["direction", "delimiter", "hasHeader", "typeInference"],
persistentOptionReview: {
hasHeader: "Boolean CSV parsing toggle; it does not contain header names or row data.",
},
validateOptions(options) {
const direction = stringOption(options, "direction", "csv-to-json")
if (!["csv-to-json", "json-to-csv"].includes(direction)) return fail("direction must be csv-to-json or json-to-csv.")
Expand Down Expand Up @@ -518,6 +532,9 @@ const unixTimestampAdapter: PipelineToolAdapter = {
output: "iso",
},
publicOptionKeys: ["output"],
persistentOptionReview: {
output: "Bounded enum controlling timestamp output format; it does not contain output data.",
},
validateOptions(options) {
const output = stringOption(options, "output", "iso")
if (!["iso", "json"].includes(output)) return fail("output must be iso or json.")
Expand Down Expand Up @@ -582,6 +599,7 @@ const regexTesterAdapter: PipelineToolAdapter = {
maxMatches: 100,
},
publicOptionKeys: ["pattern", "flags", "maxMatches"],
persistentOptionKeys: ["flags", "maxMatches"],
validateOptions(options) {
const pattern = stringOption(options, "pattern", "")
const flags = stringOption(options, "flags", "g")
Expand Down Expand Up @@ -659,6 +677,7 @@ const jsonSchemaAdapter: PipelineToolAdapter = {
schema: "",
},
publicOptionKeys: ["mode", "schema"],
persistentOptionKeys: ["mode"],
validateOptions(options) {
const mode = stringOption(options, "mode", "generate")
if (!["generate", "validate"].includes(mode)) return fail("mode must be generate or validate.")
Expand Down
Loading