본문 바로가기
C.W.K.
Stream
Lesson 03 of 05 · published

Host permission — URL match pattern과 install 경고

~11 min · host_permissions, url-pattern, all_urls, install-warning

Level 0Extension 입덕
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Manifest의 모든 host pattern이 만지려고 permission 요청하는 인터넷의 한 평방마일. Lesson 3가 syntax, install prompt 비용, 가장 무서운 경고 최소로 가장 큰 reach 위해 ClipDeck design 하는 법."

URL match pattern syntax

Match pattern이 세 부분: scheme / host / path. :///로 join:

<scheme>://<host>/<path>

예:

  • https://github.com/* — github.com root과 모든 path, HTTPS only.
  • https://*.github.com/* — github.com AND 모든 subdomain (gist.github.com, api.github.com, ...).
  • https://github.com/anthropics/* — anthropics org 아래 repository만.
  • *://*/* — Chrome이 제한하는 거 (chrome://, 등) 외 모든 scheme, 모든 host, 모든 path. 몇 edge case 빼고 <all_urls>와 동등.
  • <all_urls> — explicit wildcard. HTTP / HTTPS / FTP / file: 매칭.

규칙:

  • scheme 자리에는 http, https, file, ftp, 아니면 *가 와. 평범한 extension 이라면 chrome://chrome-extension://은 무슨 수를 써도 안 걸려.
  • Host: literal, OR * 혼자 (모든 host), OR *. prefix (subdomain wildcard). Host 가운데 wildcard (foo*.example.com) 안 허용.
  • Path: literal 문자와 * wildcard. Path 필수. "모든 path" 면 /* 사용.

Install 경고 사다리

Chrome의 prompt가 host가 얼마나 넓은지에 따라 scale:

  • 한 특정 host (https://github.com/*) → "Read and change your data on github.com."
  • Subdomain wildcard (https://*.github.com/*) → "Read and change your data on sites in the github.com domain."
  • 여러 특정 host (https://github.com/*, https://gitlab.com/*) → "Read and change your data on github.com and gitlab.com." Chrome이 작은 한계까지 list, 다음 collapse.
  • <all_urls>*://*/* → 시끄러운 "Read and change all your data on all websites."

"3 host" 와 "all websites" 사이 user 신뢰 drop이 가파름. 가능하면 host 좁게 cluster, 정말로 everywhere 동작해야 할 케이스에 <all_urls> 예약.

Chrome의 'Runtime Host Permission' twist

Chrome 70 쯤부터, user가 puzzle-piece menu 나 chrome://extensions 통해 installed extension의 site access 변경 가능: "On click," "On <specific site>," "On all sites." Manifest가 host_permissions: ["<all_urls>"] 선언해도, user가 "On click" 만으로 제한할 수 있어 — extension이 activeTab 처럼 동작하게 만듦.

여기서 따라 나오는 게 있어. manifest에 host 권한을 적어 뒀다고 해서 지금도 그렇다고 코드가 가정하면 안 돼. 중요한 자리라면 chrome.permissions.contains로 확인하든지, 아니면 host를 건드리는 기능 전부를 거절당해도 버티게 설계해. scripting 호출에서 'Cannot access contents of the page' 가 돌아오면 그걸 잡아서 이유를 설명해 주는 식으로.

Content-script match pattern

content script는 content_scripts 아래에 자기 matches 배열을 따로 갖고 있어. 문법은 같은데 host_permissions 와는 별개로 열리는 권한이야:

  • host_permissions가 programmatic inject (chrome.scripting), fetch, request 관찰할 권한 부여.
  • content_scripts.matches가 매 page load 시 static, automatic injection 선언.

Chrome이 경고 목적으로 둘 union: host_permissions: ["<all_urls>"] OR content_scripts.matches: ["<all_urls>"]가 같은 무서운 경고 trigger. Host 좁히려면 두 field 함께 좁히기.

ClipDeck의 선택

ClipDeck v0.9가 content_scripts.matches: ["<all_urls>"] 가짐 — floating 버튼과 selection capture가 user 방문 모든 page에서 동작해야 해서. 경고 부드럽게 하는 두 방법:

  • 항상-on content script 완전 drop. Toolbar click 이나 hotkey 시 activeTab + programmatic chrome.scripting.executeScript 사용. 비용: 매 page의 floating 버튼 없음, user가 부른 후만. 이득: install 경고 없음, 훨씬 친절.
  • content script는 그대로 두되 민감한 곳은 콕 집어 빼기. 닿는 범위는 넓게 유지하면서, 어떤 부류는 아예 안 건드린다고 user 한테 말해 주는 거야. 경고 문구는 그대로인데 신뢰를 주는 신호는 훨씬 세.

ClipDeck v1은 두 번째 경로 — floating 버튼이 discovery 이야기 일부라서. v2가 privacy-minded 무리 위해 activeTab-only mode로 전환하는 per-user toggle 추가할 수도.

Match pattern: scheme + host + path. 좁은 host = 친절한 install 경고. <all_urls>가 어떤 prompt의 가장 시끄러운 줄 — 자격을 얻어. User가 runtime에 host downgrade 가능. 그것 위해 design.
User가 실제로 보는 거 테스트. chrome://extensions → Developer mode → Pack extension → Pack이 .crx만 생성. 일반 Chrome profile에 그거 install (또는 chrome://extensions → Drag and drop). 뜨는 dialog가 user가 보는 정확한 install prompt. Permission set이 eye test 통과하는지 저렴하고 즉각적 feedback.

Code

GitHub-specific helper 위한 좁게-scoped host_permissions·json
{
  "host_permissions": [
    "https://github.com/*",
    "https://*.github.com/*",
    "https://gist.github.com/*"
  ]
}
Privacy-sensitive 명시적 exclusion 가진 넓은 matches·json
{
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "exclude_matches": [
        "https://accounts.google.com/*",
        "https://*.googleusercontent.com/*",
        "https://*.bank.com/*",
        "https://*.chase.com/*",
        "https://*.passwordmanager.com/*"
      ],
      "js": ["content.js"],
      "run_at": "document_idle"
    }
  ]
}
background.js — executeScript denial의 defensive wrapper·javascript
// background.js — user 가 runtime 에 host access downgrade 처리
async function safeInject(tabId, fileOrFunc) {
  try {
    return await chrome.scripting.executeScript({
      target: { tabId },
      ...(typeof fileOrFunc === "function" ? { func: fileOrFunc } : { files: [fileOrFunc] }),
    });
  } catch (err) {
    if (String(err.message).includes("Cannot access")) {
      // URL 이 restricted (chrome:// 등) 이거나 OR user 가 이 site 의
      // 이 extension 의 site access revoke.
      console.warn("[ClipDeck SW] cannot inject:", err.message);
      return null;
    }
    throw err;
  }
}

External links

Exercise

clipdeck/manifest.json의 content_scripts entry에 두 번째 code block의 explicit exclude_matches list 추가. Reload. 은행이나 password-manager URL (또는 exclude list의 어느 거) 방문 — floating 버튼 안 나타나고 SW DevTools에 [ClipDeck content] 줄 안 로깅되는지 확인. 다음 chrome://extensions → ClipDeck → 'This can read and change site data' 에서 'On all sites' 에서 'On click' 으로 전환. 이제 실제 page 방문 — ClipDeck 안 동작 보임. toolbar-icon click은 여전히 capture 해야 함 (activeTab 통해). 세 번째 code block의 safeInject helper를 background.js에 추가하고 chrome.scripting.executeScript 현재 있는 곳에 wire — wrapper가 runtime-revoke-access error를 exception 대신 silent return으로 바꿈.
Hint
Floating 버튼이 excluded site 에서도 여전히 나타나면, exclude_matches pattern이 틀릴 수도 — Chrome이 literal 매칭하고, https://*.bank.com/*https://bank.com/ (subdomain 없음)에 매칭 안 함. Apex 필요하면 https://bank.com/* 명시적 추가. 'On click' 으로 전환이 side panel도 깨면, 옛 Chrome 에선 실제로 예상 — 새 Chrome은 panel content가 restricted tab 에서도 렌더되게 (panel이 extension-owned) 하지만, page에 대한 chrome.scripting.executeScript 는 여전히 reject. safeInject wrapper가 rejection을 silently 처리. deny 되는 action이 user-initiated 면 user-facing 메시지 surface.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.