Skip to content

NicoValentine7/autofill-browser

Repository files navigation

autofill-browser

Chrome拡張の自動入力機能と、Android向け自動入力専用ブラウザを同じリポジトリで育てるための初期土台です。

ねらい

  • Chrome拡張で、デスクトップ上のフォーム自動入力を早めに検証する
  • Android側で、WebViewベースの専用ブラウザを薄く立ち上げる
  • 将来的に、入力ルールや保存データの扱いを共有できる構成に寄せる

現在の構成

.
├── apps
│   ├── android-browser
│   │   └── app
│   ├── chrome-extension
│   └── log-worker
├── packages
│   └── autofill-core
├── docs
│   └── product-outline.md
├── package.json
└── pnpm-workspace.yaml

ディレクトリ説明

  • apps/chrome-extension
    • Plasmoベースの最低限の拡張機能雛形
    • コンテンツスクリプトとポップアップUIを配置
  • apps/android-browser
    • Kotlin + Android WebView の最低限のアプリ雛形
    • まだ AutofillService 連携までは入れていない
  • apps/log-worker
    • Cloudflare Workers + D1 のログ保存・Google同期API
    • Chrome拡張から送られたイベントログ、設定snapshot、Remote rules、日次解析レポートを保存する
  • packages/autofill-core
    • 自動入力ルールと判定ロジックの共有パッケージ
    • src/autofill-rules.json を Android 用 asset と同期して使う
  • docs/product-outline.md
    • 解像度低めの要求整理と今後の叩き台

使い方

Chrome拡張

cd apps/chrome-extension
pnpm install
pnpm dev

Plasmo のビルド出力は build/chrome-mv3-dev/ または build/chrome-mv3-prod/ に出ます。Chrome の拡張機能管理画面から「パッケージ化されていない拡張機能を読み込む」で対象ディレクトリを指定してください。

現在の Chrome 拡張は、packages/autofill-core のルールを読んで手動トリガーでフォーム入力を試す最小構成です。

Chrome拡張は標準でこのリポジトリの Cloudflare Worker に接続するため、保存先URLの入力は不要です。Googleログインすると、Cloudflare D1を正本としてプロフィール、自動入力設定、ドメイン制御、イベントログ、Remote rulesを自動同期します。Chromeのローカルstorageはオフライン用キャッシュです。

開発中の読み込み運用

Chrome に読みっぱなしにする通常のローカル確認では、HMR を含まない unpacked build を使います。chrome://extensions では apps/chrome-extension/build/chrome-mv3-dev/ を読み込んでください。この出力は prod 相当のバンドル設定で生成するため、Plasmo dev server を起動していない状態でも ws://localhost:* の接続エラーは出ません。

pnpm build:extension:unpacked

ホットリロードが必要な時だけ pnpm dev:extension を常駐させます。plasmo dev は同じ build/chrome-mv3-dev/ に HMR 付きの出力を生成するため、dev server を止めた後にそのまま使い続けると Chrome の拡張機能エラー画面に WebSocket 接続失敗が残ります。dev server を止めた後は、再度 pnpm build:extension:unpacked を実行してから Chrome 側で拡張をリロードしてください。

pnpm dev:extension

本番相当の最終確認や別PC配布前だけ、pnpm build:extension を実行して apps/chrome-extension/build/chrome-mv3-prod/ を使います。

機密フィールドの扱い

自動入力は、通常プロフィール、学習済み任意フィールド、銀行/カード系フィールドを同じ候補収集層で扱います。ただし、機密度で保存・ログ出力の扱いを分けます。

  • 支店番号、口座番号、カード番号、有効期限、カード名義は Secure Vault に学習し、popupからの手動実行時に入力できます
  • CVC/CVV/CID/セキュリティコードは将来利用のために保存せず、学習も自動入力もしません
  • 銀行/カード系の field_learned_from_user / field_filled / field_corrected_by_user イベントは、previousValue / nextValue を保存せず values:redacted だけ残します
  • API tokenは、ユーザーがpopupで明示作成したcopy-onlyのSecure Vault itemとして保存・更新・コピー・削除できます。token本体、サービスURL、アカウント、メモは暗号化値として保存し、token っぽいフォームフィールドからの自動学習・自動入力はしません
  • PIN、パスワード、OTP、captcha、CSRF token、合言葉/秘密の質問系は学習も自動入力もしません
  • Secure Vault は通常の fieldMemory と分離し、ローカルでは AES-GCM で暗号化して保存します。Google同期では暗号化済みのVault dataだけをD1へ保存し、Vault KeyはWorkerにもD1にも送信しません。Vault Keyは chrome.storage.session にだけ保持し、過去版の chrome.storage.local に残ったキーは起動時にsessionへ移してlocal側を空にします
  • API tokenのコピーはpopupの明示操作だけで実行し、manifestでは clipboardWrite を要求します。clipboardからの読み取りはしません
  • 別PCでは、Googleログイン後にSecure Vaultの回復フレーズを入力すると、D1上のVault Recovery PackageからVault Keyをこの端末へ復元できます。回復フレーズは拡張側で高エントロピー生成し、PBKDF2-SHA256 600k iterations + AES-GCM AADでVault Keyを包み、保存・送信しません
  • Secure Vaultには vaultId, activeKeyId, encrypted key-check canary を持たせ、復元時はVault Recovery Packageの vaultId とcanary検証に通ったVault Keyだけを端末sessionへ保存します

Agent Vault for Codex / Claude Code

Codex や Claude Code から開発用 API token を使う場合は、Rust CLI の agvt を使います。Agent Vault は hybrid 方針です。共通tokenは global vault (~/.local/share/agvt/agent-vault.json、または AGVT_GLOBAL_PATH) に保存し、repo固有tokenは repo-local vault (.local/agent-vault.json、または AGVT_PATH) に保存します。.local/ はgit管理外です。token本体、service URL、account、account ID、notesは暗号化payloadに入り、agvt ls ではvault名・item名・kind・label・更新日時だけを出します。

export AGVT_PASSPHRASE="24文字以上のローカルpassphrase"
pnpm agvt keychain set

export CLOUDFLARE_API_TOKEN="<cloudflare-api-token>"
export CLOUDFLARE_ACCOUNT_ID="<cloudflare-account-id>"
pnpm agvt add agvt://global/cloudflare/token
pnpm agvt --vault global run cloudflare -- npx wrangler whoami

export OPENAI_API_KEY="<openai-api-key>"
pnpm agvt add agvt://global/openai/token
pnpm agvt --vault global run openai -- sh -c 'test -n "$OPENAI_API_KEY"'

pnpm agvt import-env --dry-run
pnpm agvt import-env
pnpm agvt prepare --dry-run

GITHUB_TOKEN=agvt://global/github/token pnpm agvt run -- gh auth status
pnpm agvt read agvt://global/cloudflare/account-id
pnpm agvt read agvt://global/cloudflare/token
pnpm agvt inject --redact-output .env.template

cloudflareopenaianthropicvercelstripeslackgithub はプリセットです。pnpm agvt presets --json で確認できます。cloudflareCLOUDFLARE_API_TOKEN に加えて、保存済みなら CLOUDFLARE_ACCOUNT_IDrun で渡します。その他のprovider presetは既存tokenを保存・注入するだけで、token作成・更新・検証・権限探索はしません。agvt://cloudflare/token のような短縮secret referenceは無効です。agvt://global/cloudflare/tokenagvt://repo/cloudflare/token のようにvault名を明示してください。カスタムtokenは --from-stdin--from-env TOKEN_ENV_NAME を使い、tokenをコマンド引数に直接載せないでください。

import-env は、現在の環境変数と、存在する .env.local / .env.development / .env.production / .env から、プリセットやsecretっぽい名前の値をVaultへ取り込みます。値は表示しません。まず --dry-run で取り込み候補のenv名だけを確認できます。custom importでは *_TOKEN*_API_KEY*_SECRET*_SECRET_KEY*_SERVICE_ROLE_KEY*_PASSWORDDATABASE_URL を拾い、NEXT_PUBLIC_* / PUBLIC_* は除外します。custom importを避けたい場合は --preset-only を使います。

prepare は、repoで必要なsecretがVaultにあるかを診断するdry-run中心の入口です。agvt.toml があれば [prepare] presets = ["cloudflare"][[prepare.secrets]] を読み、なければ wrangler など既知のrepo hintや .env.* のenv名から候補を出します。Vaultへの保存、.env の書き換え、Cloudflare token自動発行はしません。出力は present / importable / missing / unchecked とenv名・次のコマンドだけで、secret値は表示しません。

[prepare]
vault = "global"
presets = ["cloudflare"]

[[prepare.secrets]]
item = "admin-secret"
field = "token"
env = "ADMIN_SECRET"
required = true

add / delete はVault file単位のlockを取り、同じVaultへ複数の agvt processが同時に書き込んでもlast-write-winsでitemを失わないようにします。inject はsecret値を標準出力へ展開するため、確認だけなら --redact-output を使ってください。

AGVT_PASSPHRASE が未設定の場合、macOSでは agvt keychain set で保存したpassphraseをKeychainから読みます。Keychain accountはvault path単位です。global vault のpassphraseを明示的に登録する場合は --vault-path "$HOME/.local/share/agvt/agent-vault.json" を付けます。Keychainを使わない場合は AGVT_KEYCHAIN=0 を付けます。

printf '%s' "$AGVT_PASSPHRASE" | pnpm agvt keychain set --from-stdin
pnpm agvt keychain status
unset AGVT_PASSPHRASE
pnpm agvt ls

run は指定したsecretだけを子プロセスへ渡し、AGVT_PASSPHRASEAUTOFILL_AGENT_VAULT_PASSPHRASE は子プロセス環境から外します。漏洩を減らしたい時は --clean-env--redact-output を使います。macOSでは --sandbox no-network でネットワーク送信をベストエフォートで塞げます。

pnpm agvt --vault global run cloudflare --clean-env --redact-output -- npx wrangler whoami
pnpm agvt --vault global run github --sandbox no-network -- gh auth status

Cloudflare tokenは、発行権限を持つfactory tokenとpolicy fileがあれば agvt から作成して、そのまま暗号化保存できます。policy bodyはCloudflare APIの /user/tokens 作成形式に合わせます。自動発行はCloudflare専用の明示フローとして維持し、OpenAI、Anthropic、Vercel、Stripe、Slack、GitHubでは既存tokenの保存と注入だけを扱います。

export CLOUDFLARE_TOKEN_FACTORY_TOKEN="<token-create-capable-token>"
pnpm agvt cloudflare create-token agvt://global/cloudflare/token \
  --name "agvt cloudflare dev" \
  --account-id "$CLOUDFLARE_ACCOUNT_ID" \
  --policy-file docs/agvt-cloudflare-token-policy.example.json

API token以外のkindも保存できます。

pnpm agvt add agvt://repo/github-totp/secret --kind totp --from-env TOTP_SECRET --field issuer=GitHub --account nico
pnpm agvt totp agvt://repo/github-totp/secret

printf '%s' "$SSH_PRIVATE_KEY" | pnpm agvt add agvt://repo/github-ssh/private-key --kind ssh-key --field-stdin private-key --field public-key="$SSH_PUBLIC_KEY"
pnpm agvt read agvt://repo/github-ssh/private-key

単体コマンドとして使う場合は以下で入れます。

pnpm install:agvt
agvt add agvt://global/cloudflare/token
agvt --vault global run cloudflare -- npx wrangler whoami

Chrome popupのAPI Token Vaultは、native hostを入れると保存時に同じtokenをAgent Vaultへも保存できます。popupでは Agent Vault scope (repo-local / global) とitem名を選びます。host未インストール時はSecure Vault保存だけ続行します。

pnpm install:agvt
pnpm install:agvt-native-host
pnpm build:extension

CLIの実動作確認は以下です。

pnpm test:agvt

helpは英語/日本語を選べます。AGVT_LANG=ja を設定すると agvt help の既定表示を日本語にできます。

agvt help ja
agvt help en
AGVT_LANG=ja agvt help

拡張IDを固定する

manifest key の公開鍵は apps/chrome-extension/package.json に入れてあるため、別PCでも clone して build するだけで同じ拡張IDになります。これは「unpackedで読み込むローカル版」のための固定IDです。秘密鍵のコピーは不要です。

pnpm build:extension

固定IDを確認したい場合は以下を実行してください。

pnpm setup:extension-key

pnpm setup:extension-key は、公開鍵が既に repo にある場合は固定IDを表示するだけです。CRX署名用の秘密鍵を作り直したい場合だけ pnpm setup:extension-key -- --rotate を使ってください。秘密鍵は apps/chrome-extension/.extension-key/ に置かれ、git 管理しません。

別PCに入れる

別PCでは秘密鍵や .env.local を同期しなくて大丈夫です。repo に固定ID用の公開 manifest key が入っているため、clone して build するだけで同じ拡張IDになります。

git clone https://github.com/NicoValentine7/autofill-browser.git
cd autofill-browser
pnpm install
pnpm build:extension

Chrome の拡張機能管理画面で「デベロッパーモード」をONにし、「パッケージ化されていない拡張機能を読み込む」から apps/chrome-extension/build/chrome-mv3-prod/ を選んでください。

固定される拡張IDは cjdfbkbfiengbkpejnjecgdgagipjkdk です。標準のCloudflare Worker URLは拡張側に入っているため、別PCで最初に必要なのは基本的にGoogleログインだけです。ログインすると、Cloudflare D1の最新snapshotが自動で反映されます。Secure Vaultの値を復号するには、popupで生成・保存した回復フレーズを入力してVault Keyを復元してください。

Chrome Web Store に出す

Chrome Web Store にアップロードするzipには manifest key を含められません。Web Store版はStore側で別のIDが割り当てられるため、unpacked固定ID版とは別のGoogle OAuth clientを使います。

  • unpacked固定ID: cjdfbkbfiengbkpejnjecgdgagipjkdk
  • Chrome Web Store draft ID: baanlacmimdcafhjondbnjnigjglmcph

Web Store用の素材とzipは以下で作ります。

pnpm assets:webstore
pnpm build:extension
pnpm package:extension
WEBSTORE_GOOGLE_OAUTH_CLIENT_ID=<web-store-client-id.apps.googleusercontent.com> pnpm package:webstore

生成される dist/releases/autofill-browser-chrome-v<version>-webstore.zip をDeveloper Dashboardへアップロードしてください。pnpm package:webstore はzip内の manifest.key を削除し、WEBSTORE_GOOGLE_OAUTH_CLIENT_ID が指定されていればzip内の oauth2.client_id だけWeb Store用client IDへ差し替えます。ソース側のmanifestはunpacked固定ID版のまま維持します。

Developer Dashboard のプライバシーポリシーURLには、Workerが公開する https://autofill-browser-log-worker.y-elucidator.workers.dev/privacy を指定します。このページは認証なしで表示でき、拡張の同期・ログ・Secure Vaultの扱いをWeb Store審査向けに説明します。

Googleログインと設定同期

Googleログインを使うと、初回ログイン時にAutofill Browser側のSystem Accountを作成し、そのGoogleアカウントをLinked Google Accountとして紐づけます。プロフィール、自動入力設定、ドメイン制御はSystem Account単位でCloudflare D1へ保存します。Googleアカウントはログイン手段であり、データの所有者はSystem Accountです。

プロフィールとログの入力値はWorker secretの CLOUD_DATA_ENCRYPTION_KEY でAES-GCM暗号化してD1へ保存します。Secure VaultはZero-Knowledge Vaultとして扱い、クライアントで暗号化済みのVault dataだけを同期します。Vault Keyは端末の chrome.storage.session にだけ保持し、同期snapshotやWorker保存データには含めません。Secure Vault rootには vaultId, activeKeyId, encrypted key-check canary を持たせ、別Vaultや別KeyのRecovery Packageを誤って復元しないようにします。Workerは inbound payload に secureVaultKey が残っている場合は 400 で拒否します。Worker URLや共有トークンはユーザー設定に含めません。

別PCではGoogleログインでプロフィールや設定を復元できます。Secure Vaultの値は暗号化済みdataとして同期され、Vault Keyは拡張側で生成した回復フレーズで包まれたVault Recovery Packageとして同期されます。別PCのpopupで回復フレーズを入力すると、その端末のsession storageへVault Keyを復元します。

旧実装で万一 secureVaultKey を含む同期rowが残っている場合は、admin token付きで POST /admin/sync-vault-scrub を実行すると、current/history rowのlegacy keyを再暗号化または削除してscrubできます。

同期snapshotには deviceId, baseRevision, changedFields が含まれます。別PCで同時編集が起きた場合、変更フィールドが被らなければWorker側で差分マージし、同じフィールドが競合した場合はクラウド側snapshotを返して拡張側が反映します。

Google OAuth client は Google Cloud Console で作成します。Application type は Chrome extension です。Item ID は、対象に応じて以下を指定します。

  • unpacked固定ID版: cjdfbkbfiengbkpejnjecgdgagipjkdk
  • Chrome Web Store版: baanlacmimdcafhjondbnjnigjglmcph

unpacked固定ID版のclient IDを作成したら、repo内のmanifestとWorker設定へ反映します。

pnpm set:google-oauth-client <client-id.apps.googleusercontent.com>

Web Store版のclient IDを作成したら、Workerの許可リストへ追加します。ソースmanifestは更新せず、Web Store zip作成時にだけclient IDを差し替えます。

pnpm set:google-oauth-client <web-store-client-id.apps.googleusercontent.com> --webstore
WEBSTORE_GOOGLE_OAUTH_CLIENT_ID=<web-store-client-id.apps.googleusercontent.com> pnpm package:webstore

その後、拡張とWorkerをbuild/deployします。

pnpm build:extension
pnpm --dir apps/log-worker migrate:remote
pnpm deploy:log-worker

標準のWorker URLは apps/chrome-extension/lib/cloud-config.ts に入っています。Googleログイン時は /me/me/settings を使い、ログ送信は /me/events にGoogle tokenでPOSTします。

CloudflareログAPI

Cloudflare側は apps/log-worker にあります。D1 databaseを作り、apps/log-worker/wrangler.jsoncdatabase_idGOOGLE_OAUTH_CLIENT_ID を差し替えてからmigrationとsecret設定を行ってください。

npx wrangler d1 create autofill-browser-logs
pnpm --dir apps/log-worker migrate:remote
npx wrangler secret put CLOUD_DATA_ENCRYPTION_KEY --config apps/log-worker/wrangler.jsonc
npx wrangler secret put CLOUD_LOG_INGEST_TOKEN --config apps/log-worker/wrangler.jsonc
pnpm deploy:log-worker

localで試す場合は apps/log-worker/.dev.varsCLOUD_DATA_ENCRYPTION_KEY=local-dev-encryption-keyCLOUD_LOG_INGEST_TOKEN=local-dev-token を置き、以下を実行します。

pnpm --dir apps/log-worker migrate:local
pnpm dev:log-worker

ユーザー向けAPIは、ログイン確認が GET /me、設定同期が GET /me/settingsPUT /me/settings、ログが POST /me/eventsGET /me/events?limit=50、Remote rulesが GET /me/rules、ログ解析が GET /me/log-analysis?limit=7 です。いずれもGoogle access tokenの Authorization ヘッダーが必要です。

共有トークンで使う管理用APIは GET /admin/logs?limit=50GET/PUT /admin/rulesGET /admin/log-analysis?limit=7 です。wrangler.jsonc には毎日 15:00 UTC、つまり日本時間 00:00 のCron Triggerを設定しており、直近24時間の全体ログ解析を log_analysis_reports に保存します。

管理画面は Worker 内蔵の GET /admin です。画面に CLOUD_LOG_INGEST_TOKEN を入力すると、最近のログ、日次ログ解析、Remote rules、同期履歴を確認できます。tokenはブラウザのlocalStorageにだけ保存されます。

この端末では管理用tokenを .local/cloud-admin-token.txt に置いています。.local/ はgit管理外です。

設定同期は最新snapshotを user_sync_snapshots に持ちつつ、保存・restoreの履歴を user_sync_snapshot_history に残します。Googleユーザー向けには GET /me/settings/history?limit=20POST /me/settings/history があり、POST bodyに { "revision": 1 } のように指定すると、そのrevisionのsnapshotへ巻き戻せます。

Google同期の実Worker E2Eは以下で実行できます。VERIFY_D1_RAW=1 を付けるとWranglerでD1の生値も確認し、テスト用markerが暗号化済みprofileに平文で残っていないことを検査します。

GOOGLE_ACCESS_TOKEN=<google-access-token> pnpm verify:google-sync-e2e
GOOGLE_ACCESS_TOKEN=<google-access-token> VERIFY_D1_RAW=1 pnpm verify:google-sync-e2e

テストサイト

pnpm serve:test-site

そのあと http://localhost:4173 を開くと、以下をまとめて試せます。

  • 日本語の基本プロフィールフォーム
  • 姓 / 名 / 市町村 / 住所欄1 / 住所欄2 が分離された checkout
  • 英語ラベルの checkout と street-address
  • 分割郵便番号 / 分割電話番号
  • placeholder や aria-label しか無い legacy フォーム
  • username / nickname / cardholder name みたいな誤爆トラップ
  • MutationObserver 向けの遅延表示フォーム

Androidブラウザ

Android Studio で apps/android-browser を開いてください。

このマシンでは Java / Gradle がまだ入っていないため、CLIビルド検証は未実施です。

共有ルールを Android asset に反映する場合は以下を実行してください。

pnpm sync:android-rules

次にやると良さそうなこと

  1. Android 側で共有ルール JSON を読み込む repository を追加する
  2. Android 側の対象を「一般ブラウザ」ではなく「自動入力専用フロー」に絞って画面遷移を定義する
  3. Chrome拡張のフォーム検出ロジックを段階的に増やす

About

Browser autofill prototype with Chrome extension and Cloudflare log sync

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors