スキル一覧に戻る

decode-minified-js-gates

pjt222
更新日 2 days ago
2 閲覧
17
2
17
GitHubで表示
開発general

について

このスキルは、難読化されたJavaScriptを分析し、機能フラグの実装を特定・分類します。様々なゲート呼び出しのバリエーションを検出し、デフォルト値とロジックを抽出します。フラグの動作が不明確な場合や複数のリーダーライブラリが使用されている場合に有用な、構造化されたメカニクスレコードを生成します。主な機能には、コンテキスト抽出、バリエーション特定、論理積やキルスイッチ反転の検出が含まれます。

クイックインストール

Claude Code

推奨
メイン
npx skills add pjt222/agent-almanac -a claude-code
プラグインコマンド代替
/plugin add https://github.com/pjt222/agent-almanac
Git クローン代替
git clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/decode-minified-js-gates

このコマンドをClaude Codeにコピー&ペーストしてスキルをインストールします

ドキュメント

Decode Minified JS Gates

Read the call-site context around a flag string in a minified JavaScript bundle and produce a gate-mechanics record: which reader variant, what default, what conjunction, what role. Where probe-feature-flag-state answers "is this gate on or off?", this skill answers the prerequisite question — "what does this gate actually do?"

When to Use

  • A flag surfaced by sweep-flag-namespace cannot be classified from its name alone.
  • The binary uses more than one gate-reader function and you need to know which one a flag invokes.
  • A gate's "default" appears non-boolean ({}, null, a numeric literal) and you need to decode the actual reader variant.
  • You suspect a kill-switch (inverted gate) but cannot confirm from the flag name.
  • A predicate combines multiple gates with && and you need to enumerate the co-gates before probing any of them.

Inputs

  • Required: a minified JavaScript bundle file (.js, .mjs, .bun).
  • Required: a target flag string to decode, in literal form.
  • Optional: a list of known reader function names from a prior decode pass — speeds Step 2.
  • Optional: a context-window size override; default is 300 chars before, 200 chars after the flag occurrence.

Procedure

Step 1: Extract the Context Window

Locate the flag string and capture an asymmetric window around each occurrence. The pre-context (before the flag) is where the reader function name lives; the post-context (after) is where the default value and conjunction live.

BUNDLE=/path/to/cli/bundle.js
FLAG=acme_widget_v3                   # synthetic placeholder
PRE=300
POST=200

# All byte offsets where the flag string occurs
grep -boE "\"${FLAG}\"" "$BUNDLE" | cut -d: -f1 > /tmp/decode-offsets.txt
wc -l /tmp/decode-offsets.txt

# Capture an asymmetric window per occurrence
while read -r offset; do
  start=$((offset - PRE))
  [ "$start" -lt 0 ] && start=0
  length=$((PRE + POST))
  echo "=== offset $offset ==="
  dd if="$BUNDLE" bs=1 skip="$start" count="$length" 2>/dev/null
  echo
done < /tmp/decode-offsets.txt > /tmp/decode-windows.txt

less /tmp/decode-windows.txt

For a fast first pass, grep -oE with negative lookbehind via Perl-compatible regex catches the same windows in one pipe.

Expected: one or more context windows per flag occurrence, each ~500 chars. Multiple occurrences share the same reader function but may differ in default or conjunction — inspect each independently.

On failure: if the bundle is too large for dd-per-occurrence (binary > 100MB or many occurrences), use rg -B 5 -A 3 "$FLAG" "$BUNDLE" for a structured-output approximation. If the windows look corrupted, the bundle may be UTF-16 or have non-ASCII delimiters; use iconv or treat as binary.

Step 2: Identify the Reader Variant

Minified gate libraries commonly expose 4–6 reader variants with different semantics. The reader function name is the first cue; the call signature is the verifier.

The variant taxonomy (synthetic names — substitute the actual minified identifiers from your bundle):

VariantSynthetic shapeReturnsCommon usage
Sync booleangate("flag", false) or gate("flag", true)booleanStandard on/off feature switches
Sync config-objectfvReader("flag", {key: value})JSON objectStructured config (delays, allowlists, model names)
Bootstrap-aware TTLttlReader("flag", default, ttlMs)boolean (cached)Startup-path gates before remote config arrives
Truthy-onlytruthyReader("flag")truthy/falsyQuick checks; no explicit default
Async bootstrapasyncReader("flag")Promise<boolean>Gates resolved post-bootstrap
Async bridgebridgeReader("flag")Promise<boolean>Bridge/relay-channel gates with separate evaluation path

Match each context window against the variant patterns:

# Test for variant patterns. Replace the synthetic reader names with the
# actual minified identifiers found in the bundle.
grep -oE '\b(gate|fvReader|ttlReader|truthyReader|asyncReader|bridgeReader)\("acme_widget_v3"' /tmp/decode-windows.txt | sort | uniq -c

If multiple variants appear for the same flag (rare but real — a flag read both sync at startup and async post-bootstrap), record each occurrence's variant separately. Probe results may differ.

Expected: every gate-call occurrence is tagged with one variant. Variant counts across the whole sweep produce a binary-level distribution (e.g., "60% sync boolean, 30% config-object, 10% TTL").

On failure: if a context window contains no recognizable reader pattern, the flag may not be gate-called — recheck the call-site classification from sweep-flag-namespace Step 2. If a window contains a reader name not in this taxonomy, document it as a new variant in your research artifacts and decide whether it warrants a separate handling path.

Step 3: Extract the Default Value

The default is the second positional argument to the reader (or absent for truthy-only / async variants). Capture the exact literal — false, true, null, 0, a string, or a JSON config object.

# Boolean default extraction (sync boolean and TTL variants)
grep -oE '\b(gate|ttlReader)\("acme_widget_v3",\s*(true|false)' /tmp/decode-windows.txt

# Config-object default — match the opening brace and capture until the
# matching brace at the same nesting depth. For minified bundles this is
# safe with a non-greedy match because objects rarely span lines.
grep -oE 'fvReader\("acme_widget_v3",\s*\{[^}]*\}' /tmp/decode-windows.txt

# Numeric default (rare but real for TTL or threshold gates)
grep -oE '\b(gate|ttlReader)\("acme_widget_v3",\s*[0-9]+' /tmp/decode-windows.txt

For config-object defaults, inspect the JSON structure — keys often hint at the gate's purpose (e.g., {maxRetries: 3, timeoutMs: 5000} is a retry-policy config, not a feature toggle).

Expected: an exact literal default per occurrence. Booleans are unambiguous; config-objects need a manual read of the structure.

On failure: if a config-object's matching brace falls outside the context window, increase the post-context size in Step 1. If a default appears to be a variable reference (e.g., gate("flag", x)), the default is computed at runtime — note this as DYNAMIC and probe the actual returned value via probe-feature-flag-state.

Step 4: Detect Conjunctions and Kill Switches

Many gates participate in compound predicates. Conjunctions (&&) and inversions (!) change the gate's effective role.

# Conjunction detection: gate-call followed by `&&` and another gate-call
# within the same predicate window
grep -oE '(gate|fvReader|ttlReader|truthyReader|asyncReader|bridgeReader)\("acme_widget_v3"[^)]*\)\s*&&\s*(gate|fvReader|ttlReader|truthyReader|asyncReader|bridgeReader)\("acme_[a-zA-Z0-9_]+"' /tmp/decode-windows.txt

# Kill-switch detection: leading `!` before the gate-call
grep -oE '!\s*(gate|fvReader|ttlReader|truthyReader|asyncReader|bridgeReader)\("acme_widget_v3"' /tmp/decode-windows.txt

For each detected conjunction, list the co-gate flag names. They are now part of the probe scope — if the target flag's evaluation depends on co-gates, probing the target alone produces incomplete state.

For each detected inversion, mark the flag as a kill switch in the gate-mechanics record. Kill switches flip the meaning of the default: a kill switch with default=false is "feature on by default" (because !false === true), while a normal gate with default=false is "feature off by default."

Expected: a conjunction list (possibly empty) and an inversion flag (boolean) per occurrence.

On failure: if a conjunction includes more than 2 co-gates, the predicate is complex enough that the regex misses the structure. Read the context window manually and document the predicate shape verbatim in the gate-mechanics record.

Step 5: Classify the Gate's Role

Synthesize Steps 2–4 into a role classification. Roles drive different probe strategies and different integration risk.

RoleSignatureImplication
Feature switchsync boolean, no inversion, no conjunctionStandard on/off; probe directly
Config providersync config-object (fvReader)Read returned object; default-empty {} ≠ feature off
Lifecycle guardbootstrap-aware TTL or async bootstrapState depends on bootstrap timing; probe at multiple points
Kill switchinverted gate, default-falseFeature on for users by default; flag flips it OFF
Conjunction memberany variant with && co-gateCannot evaluate alone; co-gates are part of the probe scope
Bridge gateasync bridge variantProbe must occur over the bridge channel, not the main path

Expected: every gate-call occurrence has exactly one primary role. Some flags appear in multiple roles across occurrences (e.g., a feature switch in one call site, a conjunction member in another) — record each role independently.

On failure: if a role does not fit the table, the binary is using a gate library not yet documented in this skill. Add a row with synthetic identifiers and contribute the variant back to the skill (or a project-specific extension) for future investigators.

Step 6: Produce the Gate-Mechanics Record

Combine the per-flag findings into a structured record. JSONL is convenient because each flag becomes one line, easy to merge with sweep-flag-namespace inventory.

{"flag":"acme_widget_v3","variant":"sync_boolean","default":false,"role":"feature_switch","conjunctions":[],"inverted":false,"occurrences":3}
{"flag":"acme_retry_policy","variant":"sync_config_object","default":{"maxRetries":3,"timeoutMs":5000},"role":"config_provider","conjunctions":[],"inverted":false,"occurrences":1}
{"flag":"acme_legacy_path","variant":"sync_boolean","default":false,"role":"kill_switch","conjunctions":[],"inverted":true,"occurrences":2}
{"flag":"acme_beta_feature","variant":"sync_boolean","default":false,"role":"conjunction_member","conjunctions":["acme_beta_program_active"],"inverted":false,"occurrences":1}

The gate-mechanics record feeds probe-feature-flag-state Step 2 (gate-vs-event disambiguation): the variant + role + conjunction list determines what observations count as evidence of LIVE / DARK / INDETERMINATE state.

Expected: one JSONL record per flag (or per flag-occurrence if a single flag has multiple distinct mechanics). The record is reproducible — running the procedure again against the same binary produces the same record.

On failure: if records vary across runs, an upstream step is non-deterministic. Most often this is the regex in Step 1 missing or over-matching occurrences. Lock the regexes for the duration of a campaign.

Validation

  • Step 1 produces one context window per flag occurrence; windows are ~500 chars
  • Step 2 tags each occurrence with exactly one reader variant from the taxonomy
  • Step 3 captures the exact default literal (boolean, config-object, or DYNAMIC)
  • Step 4 surfaces all conjunctions and kill-switch inversions present in the windows
  • Step 5 assigns one role per occurrence, drawn from the role table
  • Step 6 produces a JSONL gate-mechanics record that diffs cleanly across re-runs
  • All worked examples use synthetic placeholders (acme_*, gate, fvReader, etc.) — no real flag names, real reader names, or real config-object schemas
  • The record is consumable by probe-feature-flag-state (same flag identifiers, compatible field names)

Common Pitfalls

  • Reading "default" as "behavior": a gate with default=true is on by default in this binary, but server-side overrides may flip it. The default tells you the baseline; the runtime probe (probe-feature-flag-state) tells you the state.
  • Conflating config-object empty default with feature off: fvReader("flag", {}) returns an empty object as the default — but the flag is on (the gate evaluates to truthy). Treating {} as "off" misclassifies config-providers as feature switches.
  • Missing kill switches: a leading ! before the gate-call inverts the meaning. Skipping Step 4 produces a record that says "default=false, feature off by default" when the truth is "default=false, feature ON by default because of the inversion."
  • Probing one half of a conjunction: if acme_widget_v3 && acme_user_in_cohort is the predicate, probing only acme_widget_v3 and finding it LIVE does not mean the feature is live — the conjunction may still gate it off via the cohort flag.
  • Trusting reader names across versions: minified identifiers can change between major versions. The taxonomy in Step 2 is by signature (call shape, return type, default position), not by name. When a binary version changes, re-derive the reader names from a fresh decode pass.
  • Window too narrow: a 200/100 split misses config-object defaults that span 300+ chars. Defaults of 300/200 or 400/300 are safer; tighten only if the bundle is huge and the window cost matters.
  • Leaking real reader names: minified reader names sometimes look like nonsense (a, b, Yc1) and feel safe to paste verbatim. They are still findings — substitute synthetic placeholders before publishing the methodology.

Related Skills

  • probe-feature-flag-state — uses the gate-mechanics record to interpret runtime observations
  • sweep-flag-namespace — produces the candidate flag set this skill decodes
  • monitor-binary-version-baselines — tracks reader-name changes across binary versions; re-derive Step 2 patterns when baselines flip
  • redact-for-public-disclosure — how to publish gate-decoding methodology without exposing real reader names or schemas
  • conduct-empirical-wire-capture — validates the gate-mechanics record against runtime behavior

GitHub リポジトリ

pjt222/agent-almanac
パス: i18n/caveman-lite/skills/decode-minified-js-gates
0
agentsagentskillsai-assisted-developmentclaude-codeskillsteams

関連スキル

qmd

開発

qmdは、BM25、ベクトル埋め込み、およびリランキングを組み合わせたハイブリッド検索を用いて、ローカルファイルのインデックス作成と検索を可能にするローカル検索・インデックス作成CLIツールです。コマンドラインでの使用と、Claudeとの統合のためのMCP(Model Context Protocol)モードの両方をサポートしています。このツールは埋め込みにOllamaを使用し、インデックスをローカルに保存するため、ターミナルから直接ドキュメントやコードベースを検索するのに最適です。

スキルを見る

subagent-driven-development

開発

このスキルは、各独立したタスクに対して新規のサブエージェントを起動し、タスク間でコードレビューを実施しながら実装計画を実行します。レビュープロセスを通じて品質基準を維持しつつ、迅速な反復を可能にします。同一セッション内で主に独立したタスクに取り組む際に本スキルをご利用いただくことで、組み込まれた品質チェックを伴う継続的な進捗を確保できます。

スキルを見る

mcporter

開発

mcporterスキルは、開発者がClaudeから直接Model Context Protocol(MCP)サーバーを管理および呼び出せるようにします。このスキルは、利用可能なサーバーの一覧表示、引数を指定したツールの呼び出し、認証およびデーモンのライフサイクル管理を行うコマンドを提供します。開発ワークフローにおいてMCPサーバーの機能を統合およびテストする際に、このスキルをご利用ください。

スキルを見る

adk-deployment-specialist

開発

このスキルは、A2Aプロトコルを使用してVertex AI ADKエージェントをデプロイおよびオーケストレーションし、AgentCardの発見、タスク送信、およびコード実行サンドボックスやメモリバンクなどのサポートツールを管理します。Python、Java、またはGoで、順次、並列、またはループのオーケストレーションパターンを用いたマルチエージェントシステムの構築を可能にします。Google Cloud上でADKエージェントのデプロイやエージェントワークフローのオーケストレーションを求められた際にご利用ください。

スキルを見る