모든 Chrome 확장 프로그램에는 manifest.json이 들어 있고, 이 파일에는 브라우저 수준에서 가능한 기능의 상당 부분이 적혀 있습니다. 요청한 API, 호스트 패턴, 정적 콘텐츠 스크립트, 선택적 권한, 허용된 외부 연결 등이 여기에 담깁니다. 이 파일은 설치한 패키지 안에서 누구나 볼 수 있는 공개 파일입니다. 다만 이 파일만으로는 런타임 코드가 실제로 어떤 선언된 기능을 쓰는지, 어떤 데이터가 기기 밖으로 나가는지, 또는 별도의 웹 로그인 과정을 통해 어떤 계정 접근이 일어나는지는 증명할 수 없습니다.
이번 감사에서는 10개의 스크래핑 및 브라우저 자동화 확장 프로그램을 골라 살펴봤고, 그 안에 우리 제품 Thunderbit도 포함했습니다. 결과는 꽤 선명합니다. 어떤 확장 프로그램은 상시 사이트 접근 권한을 하나도 선언하지 않았고, 어떤 확장 프로그램은 clipboardRead를 포함해 13개의 권한을 선언했습니다. 이 가운데 debugger를 선언한 것은 Thunderbit뿐인데, 이는 페이지 접근이나 OAuth, 사용자가 제공한 스크립트와는 성격이 다른, 더 넓은 CDP 연결 권한입니다.
이런 차이가 곧바로 비난으로 이어지는 건 아닙니다. 폭넓은 권한은 어떤 기능을 제대로 구현하려면 사실상 유일한 선택인 경우가 많고, 반대로 권한이 좁다는 건 그 제품이 단순히 덜 많은 일을 한다는 뜻일 수도 있습니다. 핵심은 이런 차이가 매우 크고 공개되어 있는데도, 비교표에는 거의 드러나지 않는다는 점입니다.
어떻게 분석했는가
각 확장 프로그램은 Google의 업데이트 엔드포인트에서 .crx 파일로 내려받았고, 이는 Chrome이 실제로 쓰는 것과 동일한 URL입니다. 그다음 압축을 풀고 파싱했습니다. 어떤 확장 프로그램도 설치하지 않았고, 어떤 확장 코드도 실행하지 않았습니다. 이번 작업은 JSON 파일을 읽는 분석입니다.
다운로드는 대략 2초에 1개 정도의 속도로 진행했습니다. 각 .crx, SHA-256 값, 추출된 manifest.json은 모두 보관했습니다. Thunderbit도 다른 9개와 똑같은 스크립트를 거쳤고, 별도 우회 경로를 쓰지 않았기 때문에 Thunderbit의 행도 나머지와 정확히 같은 방식으로 산출됐습니다.
분석은 두 가지 증거 범주를 사용하며, 서로 분리해서 봅니다.
- 선언된 정적 동작: 호스트 패턴과
content_scripts항목, 특히matches,run_at,all_frames. - 런타임 코드가 사용할 수 있는 능력:
permissions또는optional_permissions에 적힌 API. 이 선언은 코드가 어떤 요청이나 호출을 할 수 있는지를 보여줄 뿐, 실제로 그렇게 한다는 뜻은 아닙니다.
“가장 강력한” 식의 서열은 매기지 않았습니다. debugger, userScripts, 넓은 호스트 접근, OAuth 범위, 클립보드 접근, 외부 메시징은 서로 다른 데이터에 영향을 주고 필요한 전제 조건도 다릅니다. 이 manifest만 보는 감사로는 이들을 하나의 위험 점수로 묶을 위협 모델이 제공되지 않습니다.
아래의 용량은 ZIP 항목을 모두 합산한 압축 해제 기준 총합이며, 단위는 MiB(2²⁰ 바이트)입니다.
기준일: 2026-07-29. 확장 프로그램은 계속 업데이트됩니다. 인용하기 전에 다시 확인하세요.
10개 manifest가 무엇을 선언하는가

공식 참고 문서: Chrome 권한 선언 가이드.
| 확장 프로그램 | 버전 | 압축 해제 용량 | 파일 수 | 권한 | 사이트 접근 | file:// 접근 가능 여부 |
|---|---|---|---|---|---|---|
| Axiom.ai | 5.1.0 | 37.1 MiB | 232 | 8 | http://*/* + https://*/* | ✅ |
| Table Capture | 11.0.41 | 21.1 MiB | 115 | 4 (+ 선택적 3개) | <all_urls> | ✅ |
| Magical | 3.119.1 | 16.8 MiB | 395 | 13 (+ 선택적 2개) | <all_urls> | ✅ |
| Thunderbit (우리 제품) | 4.6.4 | 15.9 MiB | 78 | 8개 인식됨 (+1개의 비인식 배열 문자열: commands) | <all_urls> | ✅ |
| Clay for Chrome | 1.0.0 | 6.3 MiB | 51 | 6 | *://*/* (자사 도메인에서만 주입) | — |
| Listly | 0.9.6 | 3.5 MiB | 84 | 7 | http://*/*, https://*/*, file:///*.html | ✅ |
| Hexomatic | 1.8.4 | 2.7 MiB | 37 | 2 | 자체 도메인만 | — |
| Agenty | 2.9.7 | 2.2 MiB | 49 | 4 | 선언 없음 | — |
| Clip to Clay | 1.8.0 | 0.7 MiB | 16 | 4 | 이름이 지정된 도메인 2개 | — |
| TexAu v2 | 1.6.6 | 0.3 MiB | 12 | 6 | 이름이 지정된 도메인 14개 | — |
후보 목록에는 2개가 더 있었지만, 따로 다뤄야 할 이유가 있어 표에서는 뺐습니다.
크기 차이가 아니라 설계 선택의 차이
Agenty는 호스트 권한을 전혀 선언하지 않고, 콘텐츠 스크립트도 하나도 포함하지 않습니다. 대신 activeTab, scripting, identity, identity.email의 4개 권한만 선언합니다. activeTab은 가장 제한적인 권한으로, 확장 프로그램을 클릭한 뒤에만 현재 탭에 접근할 수 있고, 페이지를 벗어나면 그 권한도 사라집니다. 사용자가 실행하지 않으면 페이지에서는 아무것도 동작하지 않습니다. 용량은 2.2 MiB입니다.
Axiom.ai는 http://*/*와 https://*/*를 선언하고, <all_urls>에 맞는 콘텐츠 스크립트를 주입하며, 232개 파일에 걸쳐 37.1 MiB로 압축 해제됩니다. 이는 Agenty보다 17배 크고, 방문하는 모든 페이지에 상시 접근할 수 있다는 뜻입니다.
Hexomatic은 Agenty와 비슷한 형태입니다. 권한은 2개(storage, tabs)뿐이고, 호스트 권한은 없으며, 콘텐츠 스크립트도 자사 도메인 2개로만 제한됩니다.
Clay for Chrome은 또 다른 방식으로 구분할 만합니다. 호스트 권한으로 *://*/*를 선언하지만, 콘텐츠 스크립트는 자사 도메인에만 주입합니다. 상시 능력은 넓지만, 자동 동작은 좁습니다. 권한 표만 보면 이 둘을 쉽게 헷갈릴 수 있습니다.
10개 중 5개는 디스크의 파일에도 접근할 수 있다
file:///은 웹사이트가 아닙니다. 브라우저 탭 안에 렌더링된 로컬 파일 시스템입니다. 예를 들면 열어 둔 PDF, HTML 내보내기 파일, 다운로드한 청구서가 여기에 해당합니다.
공식 참고 문서: Chrome match-pattern 문서.
세 개의 확장 프로그램은 이를 명시적으로 적어 두었습니다. 나머지 둘은 다른 방식으로 도달합니다. <all_urls>는 file: 스킴을 포함하기 때문입니다.
| 확장 프로그램 | file://에 도달하는 방식 | manifest에 file://를 명시했는가? | 위치 |
|---|---|---|---|
| Magical | 6개의 콘텐츠 스크립트 항목 중 4개가 file:///* — Chrome이 렌더링할 수 있는 모든 로컬 파일, HTML에 한정되지 않음 — 과 일치 | ✅ | 콘텐츠 스크립트, web_accessible_resources |
| Listly | file:///*.html과 일치 | ✅ | 콘텐츠 스크립트 |
| Table Capture | <all_urls> 콘텐츠 스크립트, 그리고 스킴 자체도 명시 | ✅ | web_accessible_resources |
| Axiom.ai | 단순히 <all_urls> 와일드카드 덕분 | — | 별도 명시는 없음 |
| Thunderbit (우리 제품) | 단순히 <all_urls> 와일드카드 덕분 | — | 별도 명시는 없음 |
http://*/*와 https://*/*는 file://를 포함하지 않습니다. <all_urls>와 *://*/*도 이 점에서는 다릅니다. 여기서 5개로 잡힌 항목은, 어떤 경로를 통해서든 해당 스킴에 닿는 선언을 가진 것들입니다. 선택한 와일드카드 형식의 결과가 아닙니다.
Chrome은 이를 확장 프로그램별 “파일 URL에 대한 액세스 허용” 토글 뒤에 두며, 이 토글은 기본적으로 꺼져 있습니다. 따라서 선언은 허가가 아니라 요청입니다. 10개 중 5개가 정확한 집계이고, 그 5개 중 2개는 manifest 어디에도 file://를 쓰지 않습니다: Axiom.ai와 우리 제품입니다.
이 집계에는 콘텐츠 스크립트뿐 아니라 web_accessible_resources도 포함됩니다. Table Capture는 여기서 file://*/*를 명시합니다. 이 블록을 빼면, manifest가 파일에 도달하면서도 스킴을 적지 않은 확장 프로그램들과 잘못 섞이게 됩니다.
요청되기 전까지는 보이지 않는 권한

optional_permissions는 미리 선언되지만 런타임에서 요청되므로, 설치 시 표시되는 권한 안내에는 나타나지 않습니다. 이를 사용하는 확장 프로그램은 2개이고, 그중 하나는 중요합니다.
| 확장 프로그램 | optional_permissions | 핵심 항목 |
|---|---|---|
| Table Capture | userScripts, downloads, identity | Chrome의 사용자 게이트가 활성화되면 페이지 컨텍스트에서 사용자가 제공한 스크립트를 실행할 수 있는 userScripts |
| Magical | downloads, webRequest | webRequest는 네트워크 트래픽을 관찰함 |
userScripts는 요청되기 전까지는 보이지 않으며, 권한 수만 보고서는 알 수 없습니다.
또한 이 권한은 이번 감사의 다른 권한들과 달리 게이트가 하나 더 있습니다. 이 부분을 빼면 과장된 표현이 됩니다. userScripts를 선언했다고 바로 사용할 수 있는 것이 아닙니다. Chrome에서 먼저 명시적 사용자 동작이 필요합니다. Chrome 138 이전에는 전역적으로 chrome://extensions에서 켜는 Developer Mode였습니다. Chrome 138부터는 해당 확장 프로그램의 상세 페이지에 있는 Allow User Scripts 토글로 바뀌었고, 기본값은 꺼져 있습니다. 따라서 일반 설치 환경에서는 이 기능이 선언만 되어 있을 뿐 실제로는 비활성 상태입니다. 정확히 말하면, 사용자가 Chrome의 게이트를 켠 뒤에만 Table Capture가 userScripts API를 사용할 수 있습니다. 이 감사에서는 사용자가 설정 페이지를 얼마나 자주 방문하는지, 또는 토글을 켜는지는 측정하지 않았습니다.
이 둘은 숨겨진 것이 아니라 둘 다 manifest에 있습니다. optional 블록을 무시한 표는 두 제품을 과소평가하게 됩니다.
범위만이 아니라 주입 시점도 중요하다
콘텐츠 스크립트가 어떻게 주입되는지는 잘 눈에 띄지 않지만, 그림을 크게 바꿉니다. document_start는 Chrome이 제공하는 가장 이른 훅이며, all_frames는 서드파티 임베드까지 포함합니다.
정적으로 모든 프레임에 주입하는 항목만, 선언된 시점 기준으로 정리하면 다음과 같습니다.
| 확장 프로그램 | run_at | all_frames | 콘텐츠 스크립트 매치 패턴 |
|---|---|---|---|
| Table Capture | document_start | ✅ | <all_urls> |
| Listly | document_start | ✅ | file:///*.html 및 모든 http/https |
| Axiom.ai | document_start | ✅ | 자체 도메인만 |
| Clay for Chrome | document_start | ✅ | 자체 도메인만 |
| Thunderbit (우리 제품) | document_end | ✅ | <all_urls> |
| Magical | document_idle | ✅ | file:///* 및 넓은 http/https |
| TexAu | document_idle (미설정) | ✅ | 이름이 지정된 패턴 14개 |
Table Capture와 Listly는 넓은 매치 패턴 위에서 가장 이른 훅을 사용합니다. Axiom.ai와 Clay for Chrome도 같은 시점을 쓰지만 자체 도메인에만 적용합니다. 공격성은 같고, 대상만 좁습니다.
정적 선언 기준으로는 <all_urls>와 all_frames가 매칭 범위의 상한이며, Table Capture와 Thunderbit가 이 지점에 있습니다. 다만 선언된 시점은 다릅니다. Table Capture는 document_start, Thunderbit의 정적 콘텐츠 스크립트는 document_end를 사용합니다. 런타임 API는 별개의 능력 범주이므로 이 표만으로 추론할 수 없습니다.
권한 개수는 좋은 요약 통계가 아닙니다. Table Capture는 권한이 4개뿐이라 여기서 가장 적은 편이지만, document_start에 <all_urls>와 all_frames 전체에 주입하며, 필요 시 userScripts를 추가로 사용할 수 있습니다.
누가 확장 프로그램에 메시지를 보낼 수 있는가
externally_connectable는 어떤 웹페이지나 다른 확장 프로그램이 확장 프로그램의 백그라운드 스크립트에 직접 메시지를 보낼 수 있는지를 제어합니다. 기본값은 직관과 반대입니다.
키를 생략하는 것이 더 허용적인 설정입니다. Chrome에서 externally_connectable가 없을 때의 기본값은 모든 확장 프로그램은 연결할 수 있고, 웹페이지는 연결할 수 없다는 뜻입니다. 이 키를 선언하는 건 오히려 허용 범위를 제한하는 행위입니다.
이 기준으로 보면 표는 반대로 읽힙니다.
| 확장 프로그램 | externally_connectable 선언 | 메시지를 보낼 수 있는 주체 |
|---|---|---|
| Agenty | 생략 | 허용적 기본값 — 모든 확장 프로그램 가능, 웹페이지는 불가 |
| Clay for Chrome | 생략 | 허용적 기본값 — 모든 확장 프로그램 가능, 웹페이지는 불가 |
| Clip to Clay | 생략 | 허용적 기본값 — 모든 확장 프로그램 가능, 웹페이지는 불가 |
| Listly | 생략 | 허용적 기본값 — 모든 확장 프로그램 가능, 웹페이지는 불가 |
| Table Capture | 생략 | 허용적 기본값 — 모든 확장 프로그램 가능, 웹페이지는 불가 |
| TexAu | 생략 | 허용적 기본값 — 모든 확장 프로그램 가능, 웹페이지는 불가 |
| Thunderbit (우리 제품) | 생략 | 허용적 기본값 — 모든 확장 프로그램 가능, 웹페이지는 불가 |
| Hexomatic | 8개의 확장 프로그램 ID 및 6개의 웹 출처 | 닫힌 기본값에서 두 채널 모두 개방 |
| Axiom.ai | 7개의 웹 출처, ids 없음 | 확장 프로그램 채널은 닫힘, 웹페이지 7개는 개방 |
| Magical | {"ids": [], "matches": []} | 이 목록에서 두 채널을 모두 명시적으로 닫는 유일한 확장 프로그램 |
Chrome 규칙에는 두 단계가 있습니다. manifest 참고 문서에 따르면:
| 상황 | 연결 가능한 주체 |
|---|---|
| 키 전체가 없음 | "모든 확장 프로그램은 연결할 수 있지만, 어떤 웹페이지도 연결할 수 없다" |
키는 존재하되 ids가 미설정이거나 [] | "어떤 확장 프로그램이나 앱도 연결할 수 없다" |
키는 존재하되 matches가 미설정이거나 [] | "어떤 웹페이지도 연결할 수 없다" |
허용적 기본값은 키 전체가 없을 때만 적용됩니다. 키가 존재하는 순간, 두 하위 필드는 모두 닫힌 상태가 되고, 값을 적을수록 각 채널이 열리는 범위가 넓어집니다.
Axiom.ai는 matches만 적고 키를 선언했습니다. 따라서 ids는 미설정 상태이며, 이는 어떤 확장 프로그램도 그와 메시지를 주고받을 수 없다는 뜻입니다. 즉 확장 프로그램 채널은 열린 것이 아니라 닫힌 상태입니다. 반면 열린 것은 7개의 웹 출처인데, 그중 Axiom 도메인은 하나뿐입니다. 나머지는 두 개의 출처를 특정할 수 없는 제3자(*://*.tgwc.space/*, *://*.bitmachine.co.uk/*)와 localhost, 0.0.0.0, Google APIs 호스트, 그리고 Axiom이 운영하지 않는 대형 소셜 플랫폼 하나입니다.
Hexomatic은 확장 프로그램 채널을 8개의 지정된 ID에 열어 둡니다. 키가 존재하는 상태에서는 기본값이 0개이므로, 8개를 적는 순간 8개로 늘어납니다. 여기에 6개의 matches가 또 다른 채널을 넓히는데, 그중 2개는 http://localhost:8000/*와 http://localhost:3000/*입니다. 즉, 사용자의 기기에서 해당 포트에 응답하는 모든 것은 허용 목록 안에 들어갑니다.
10개 중 7개는 우리 제품을 포함해 이 키를 아예 생략합니다. 이 7개는 확장 프로그램 간 메시징에서 허용적 기본값에 놓여 있습니다. 이 채널을 닫는 확장 프로그램은 2개뿐입니다. Magical은 ids: []로 명시적으로 닫고, Axiom.ai는 키를 선언한 뒤 ids를 적지 않음으로써 닫습니다. 두 채널을 모두 닫는 것은 Magical뿐입니다.
이 마지막 문장이야말로, permission 개수 대신 manifest를 읽어야 하는 이유입니다. 같은 확장 프로그램이라도 한 축에서는 매우 넓고, 다른 축에서는 이 집합 중 가장 엄격할 수 있습니다.
아무도 세지 않는 축: OAuth 범위
manifest에는 oauth2 블록이 들어갈 수 있고, 그 안의 scope는 다른 회사 서비스에 있는 계정에 대한 접근입니다. 이는 위에서 말한 어떤 권한과도 다른 성격의 접근이며, 권한 개수로는 전혀 드러나지 않습니다.
공식 참고 문서: Google OAuth 2.0 scope 목록.
| 확장 프로그램 | 요청한 oauth2 scope |
|---|---|
| Axiom.ai | openid, email, profile, auth/drive, auth/spreadsheets |
| Table Capture | auth/spreadsheets, auth/userinfo.email |
| Agenty | openid, email, profile |
| 우리 제품을 포함한 나머지 7개 | 선언 없음 |
https://www.googleapis.com/auth/drive는 가장 넓은 범위입니다. Google은 더 좁은 drive.file scope도 제공하는데, 이는 앱이 직접 만들었거나 사용자가 명시적으로 선택한 파일에만 접근을 허용합니다. 반면 auth/drive는 사용자의 Drive 전체를 보고, 편집하고, 만들고, 삭제할 수 있습니다. auth/spreadsheets도 계정이 접근할 수 있는 모든 스프레드시트에 대해 같은 형태입니다. Axiom.ai와 Table Capture 둘 다 Google 계정 scope와 <all_urls> 페이지 접근을 함께 선언합니다.
이 표가 증명할 수 있는 것에는 두 가지 한계가 있습니다. scope는 요청된 것이지 부여된 것이 아닙니다. Google은 동의 화면을 보여주고 사용자는 거절할 수 있으며, 확장 프로그램이 해당 API를 아예 호출하지 않을 수도 있습니다. 또한 manifest는 Chrome의 identity 흐름을 통해 수행된 OAuth만 볼 수 있습니다. 대신 웹 로그인 페이지로 보내는 확장 프로그램이라면 여기에는 아무것도 적히지 않을 수 있습니다. 7개의 0은 “이 파일에서 요청하지 않았다”는 뜻이지, “Google 계정에 접근하지 않는다”는 뜻이 아닙니다. 우리 제품도 마찬가지이며, 그래서 이 축은 좁다고 단정하기보다 보고하는 것이 맞습니다.
같은 기준에서 본 우리 확장 프로그램
Thunderbit 4.6.4도 다른 9개와 동일한 manifest 파서를 거쳤습니다. 패키지는 78개 파일에 걸쳐 15.9 MiB입니다.
선언된 정적 동작: 호스트 권한과 콘텐츠 스크립트 매치에 모두 <all_urls>가 들어 있습니다. 정적 스크립트는 document_end에서 all_frames로 실행됩니다. <all_urls>는 file:를 포함하므로, 사용자가 Chrome의 파일 접근 토글을 켜면 manifest는 로컬 파일에도 닿을 수 있습니다. Thunderbit는 externally_connectable도 생략하므로, Chrome 기본값에 따라 어떤 확장 프로그램이든 메시지를 보낼 수 있지만 웹페이지는 보낼 수 없습니다.
런타임 능력 상한: permissions 배열에는 activeTab, commands, debugger, offscreen, scripting, sidePanel, storage, tabGroups, tabs의 9개 문자열이 들어 있습니다. 이 중 Chrome이 권한으로 인식하는 것은 8개이며, commands는 최상위 manifest 키에 속할 뿐 이 배열의 항목으로는 효과가 없습니다. Thunderbit는 이 집합에서 debugger를 선언한 유일한 확장 프로그램이고, 이는 탭에 CDP를 연결할 수 있다는 뜻입니다. manifest는 런타임에서 scripting 등록도 가능하게 합니다. 이런 API들은 정적 document_end 행보다 더 넓은 능력을 만들지만, manifest만 읽어서는 Thunderbit가 특정 CDP 메서드를 호출하는지, 또는 더 이른 시점에 스크립트를 등록하는지는 알 수 없습니다.
이 구분은 그럴듯하지만 잘못된 몇 가지 비교를 막아 줍니다. 더 좁은 cookies 권한이 없다고 해서 debugger를 가진 코드가 접근할 수 있는 범위를 제한하는 것은 아닙니다. clipboardRead가 없다고 해서 클립보드 접근이 불가능하다는 증거도 아닙니다. 반대로 debugger가 있다고 해서 실제로 그 경로를 쓴다는 뜻도 아닙니다. 이런 질문에 답하려면 소스 코드 검토나 런타임 추적이 필요하고, 이번 분석에서는 둘 다 하지 않았습니다.
이 기준에서 Thunderbit는 정적 사이트 접근이 넓고, 이 집합에서 debugger를 선언한 유일한 확장 프로그램이며, 확장 프로그램 간 메시징은 Chrome의 기본값을 따릅니다. CDP, OAuth, userScripts, 클립보드, 호스트 접근을 하나의 공통 위협 모델로 묶을 수 없으므로, “가장 강력한” 단일 순위를 매길 수는 없습니다.
아무도 마케팅하지 않는 절제된 설계
TexAu는 0.3 MiB로 여기서 가장 작고, 와일드카드 대신 14개의 구체적 사이트를 명시합니다. 소셜 네트워크, 개발자 플랫폼, 퍼블리싱 플랫폼, 채팅 제품, 비즈니스 데이터 제공업체, 그리고 자사 도메인까지 포함됩니다. 콘텐츠 스크립트도 정확히 같은 14개에 매치됩니다.
이 manifest를 읽으면 확장 프로그램이 어디서 활성화되는지 정확히 알 수 있습니다. 동시에 이것은 제품 공개이기도 합니다. 대상 목록은 마케팅 문구보다 이 도구의 용도를 더 분명하게 말해 줍니다.
대신 감수해야 할 트레이드오프도 분명합니다. 이름을 적은 목록은 거기에 없는 사이트를 잡아낼 수 없고, 새로운 대상이 늘어날 때마다 릴리스가 필요합니다. 하지만 “14개의 지정 도메인”과 “존재하는 모든 URL”은 완전히 다른 제안이고, 이 둘 중 하나만 읽기 쉽습니다.
목록과 달랐던 두 제품
Captain Data의 확장 프로그램은 공개 배포되지 않습니다. Google 업데이트 엔드포인트는 해당 확장 ID에 대해 HTTP 204와 빈 본문을 반환합니다. 이는 스토어가 익명 사용자에게 제공하지 않는 항목에 대한 응답입니다. 반대로 listing 페이지 자체는 익명으로 제공됐고, HTTP 200, 509,829바이트였습니다. 여기서 확인할 수 없었던 것은 버전 문자열, 마지막 업데이트 날짜, 설치 수였습니다. 스크립트 자체 노트에 따르면 해당 위치의 null은 약한 증거에 불과하며, 로그인 차단 화면도 보이지 않았습니다. ID는 실제이고 1차 소스이므로 링크 기반 또는 비공개 배포가 가장 그럴듯하지만, 이것이 delist인지 여부까지는 구분할 수 없었고 추측하지 않겠습니다.
Dataflow Kit에는 Chrome 확장 프로그램이 아예 없습니다. 사이트에는 포인트 앤 클릭 선택이 가능한 호스팅 웹 앱과 REST API가 설명되어 있습니다. 그런데도 Chrome 확장 프로그램 후보군에 포함되어 있었는데, 이런 식으로 목록이 만들어지는 경우가 흔합니다.
신선도와 도달 범위 — 둘 다 공짜로 확인 가능하므로
스토어 메타데이터는 권한 외에 제품 선택 맥락도 제공합니다. 같은 필드가 2026-07-30에 모든 확장 프로그램에 대해 수집됐습니다.
| 확장 프로그램 | 스토어 버전 | 마지막 업데이트 | 설치 구간 |
|---|---|---|---|
| Thunderbit (우리 제품) | 4.6.4 | 2026년 7월 28일 | 200,000 |
| Listly | 0.9.6 | 2026년 7월 25일 | 100,000 |
| Axiom.ai | 5.1.0 | 2026년 7월 20일 | 100,000 |
| Table Capture | 11.0.41 | 2026년 6월 26일 | 200,000 |
| Magical | 3.119.1 | 2026년 4월 4일 | 200,000 |
| Agenty | 2.9.7 | 2026년 2월 8일 | 10,000 |
| TexAu | 1.6.6 | 2025년 8월 20일 | 7,000 |
| Clay for Chrome | 1.0.0 | 2025년 4월 9일 | 10,000 |
| Clip to Clay | 1.8.0 | 2025년 4월 8일 | 1,000 |
| Hexomatic | 1.8.4 | 2024년 9월 6일 | 3,000 |
| Captain Data | — | — | 공개 제공되지 않음 |
세 제품은 1년 넘게 새 버전을 내지 않았고, Hexomatic은 거의 2년째입니다. 확장 프로그램에서는 이 점이 라이브러리보다 더 중요합니다. Chrome은 약 4주마다 안정판을 내고, 확장 플랫폼 자체도 그 아래에서 계속 바뀌기 때문입니다. userScripts의 사용자 게이트는 Chrome 138에서 바뀌었습니다. 2024년에 마지막으로 배포된 확장 프로그램은 지금 브라우저가 강제하는 것과 다른 규칙 위에서 만들어졌다고 봐야 합니다.
이 표가 의미하지 않는 것도 있습니다. 설치 수는 Google이 구간별로 묶어 제공하므로 1,000 / 3,000 / 7,000 / 10,000 / 100,000 / 200,000 식으로만 비교할 수 있고 더 세밀한 비교는 불가능합니다. 일부 제품은 자체 마케팅에서 더 큰 수치를 내세우기도 합니다. 최근 날짜는 배포 사실에 관한 것이지, 유지보수 품질이나 권한 위험에 대한 판정은 아닙니다.
또 하나 유용한 부수 효과가 있습니다. 분석한 10개 패키지 모두에서 스토어 버전이 manifest 버전과 일치했습니다. 이번 감사가 파싱한 CRX 파일은 스토어가 오늘 실제로 제공하는 것이지, 오래된 사본이 아닙니다.
신선도는 어떤 대상을 다시 점검할 가치가 있는지에 영향을 주지만, manifest 의미를 바꾸지는 않습니다. 이름이 적힌 도메인 확장 프로그램이 1년 전에 업데이트됐더라도, 어제 배포된 와일드카드 확장 프로그램보다 페이지 표면이 더 작을 수 있습니다. 반대로 더 새 패키지가 Chrome 변경 사항을 더 빨리 따라간다면 운영상 더 나은 선택일 수도 있습니다. 후보군을 정할 때는 스토어 날짜로 재검토 대상을 고르고, manifest로는 어떤 능력을 더 자세히 봐야 하는지 판단하세요. 이 두 열을 하나의 점수로 합치지 마세요.
manifest가 말해 주지 않는 것
선언된 권한은 상한이지 행동이 아닙니다. <all_urls>는 확장 프로그램이 모든 페이지를 읽을 수 있을지도 모른다는 뜻일 뿐, 실제로 그렇게 하는지나 어떤 데이터가 기기 밖으로 나가는지는 말해 주지 않습니다. 실제로 무슨 일이 벌어지는지는 런타임 네트워크 트래픽을 봐야 알 수 있습니다. 이건 전혀 다른 작업이고, 이번 감사에서는 하지 않았습니다. 위 내용도 오용의 증거로 읽으면 안 됩니다.
관련 리뷰: Chrome 확장 프로그램 테스트 가능성 실험.
추가로 알아둘 경계는 다음과 같습니다.
- Chrome이 많은 부분을 통제합니다. 파일 접근은 기본적으로 꺼져 있고,
activeTab은 의도적으로 좁으며, 선택적 권한은 런타임 프롬프트가 필요하고, 사용자는 설치 시 권한 목록을 보게 됩니다. - 넓은 권한은 자주 필요합니다. “지금 보고 있는 어떤 페이지에서든 표를 추출”하는 도구는 이름이 정해진 도메인 목록만으로는 동작할 수 없습니다. 범위가 좁다는 건 때로는 절제이지만, 때로는 제품 자체가 더 작은 것일 뿐입니다.
- 버전 하나, 날짜 하루. 모든 수치는 2026-07-29에 제공된 패키지 기준입니다.
분석 과정에서 바로잡은 핵심 사항
공개된 집계는 간단한 manifest 파서를 만들 때 틀리기 쉬운 3가지 규칙을 사용합니다. 첫째, <all_urls>는 file: 스킴을 포함하지만 실제 파일 접근은 사용자 제어 토글 뒤에 있습니다. 둘째, web_accessible_resources.matches는 콘텐츠 스크립트와 호스트 권한과 함께 봐야 합니다. Table Capture가 file://*/*를 명시하는 곳이 바로 여기입니다. 셋째, externally_connectable는 전체 키가 없을 때와 키는 있지만 하위 필드가 비어 있을 때 기본값이 다릅니다. 위 표들은 이 규칙을 일관되게 적용했습니다.
Thunderbit의 권한 총합도 원시 문자열과 Chrome이 실제로 인식하는 권한을 구분합니다. 배열에는 9개 항목이 있지만 commands는 최상위 키에 속하므로, 여기서 사용한 유효 집계는 인식된 권한 8개와 비인식 문자열 1개입니다. 마지막으로, 정적 content_scripts 선언은 코드가 scripting.registerContentScripts나 CDP 메서드를 호출한다는 증거가 아닙니다. 그런 가능성은 런타임 능력 아래에만 나타나며, 관측된 행동으로 보지 않습니다. 원시 manifest, CRX 해시, 파서 출력은 모두 출판 부록에 연결해 두어, 독자가 본문을 그대로 믿지 않고도 이런 정규화를 검증할 수 있어야 합니다.
실제 설치 결정을 내릴 때는 최소한 네 가지 차원을 각각 따로 비교하세요. 기본적으로 어떤 페이지가 범위에 들어가는지, 추가 도달 범위를 여는 명시적 사용자 동작이 무엇인지, 어떤 브라우저나 계정 API가 열리는지, 그리고 어떤 외부 호출자가 확장 프로그램에 메시지를 보낼 수 있는지입니다. Agenty의 activeTab 모델, Table Capture의 게이트형 userScripts, Axiom.ai의 Drive scope, Thunderbit의 debugger 선언은 하나의 선형 축 위의 점이 아닙니다. 서로 다른 위협 질문에 대한 서로 다른 답입니다.
이 비교를 의도적으로 다른 4가지 설계에 적용하면 다음과 같습니다.
| 확장 프로그램 | manifest에서 보이는 페이지 범위 | 추가 게이트 | 이번 감사에서 확인한 비페이지 능력 | 외부 호출자 상태 |
|---|---|---|---|---|
| Agenty | 호스트 권한이나 정적 콘텐츠 스크립트 없음 | 사용자가 현재 탭에서 activeTab을 직접 실행 | Chrome identity 권한 | 키 생략: 모든 확장 프로그램 가능, 웹페이지는 불가 |
| Table Capture | document_start에 <all_urls> 전체 프레임 정적 스크립트 | 파일 접근은 기본적으로 꺼짐; userScripts는 Chrome의 별도 사용자 게이트 필요 | 선택적 userScripts, downloads, identity | 키 생략: 모든 확장 프로그램 가능, 웹페이지는 불가 |
| Axiom.ai | 자체 도메인에서 document_start의 HTTP/HTTPS 와일드카드 접근과 정적 스크립트 | 요청한 Google scope에 대한 OAuth 동의 | 선언된 Drive 및 Sheets OAuth scope | 확장 프로그램 호출은 닫힘; 웹 출처 7개 개방 |
| Thunderbit | document_end에 all_frames 포함 <all_urls> 호스트 접근과 정적 스크립트 | 파일 접근은 기본적으로 꺼짐; debugger 연결은 Chrome이 제어하는 사용자 가시 동작을 가짐 | debugger, scripting, tabs 및 관련 브라우저 API 선언 | 키 생략: 모든 확장 프로그램 가능, 웹페이지는 불가 |
이 표도 승자를 만들지는 못합니다. Agenty가 상시 페이지 접근을 좁게 선언했다고 해서, 제출된 데이터로 백엔드가 무엇을 하는지 알 수 있는 건 아닙니다. Axiom.ai의 계정 scope는 현재 페이지를 읽는 확장 프로그램과 직접 비교할 수 없습니다. Table Capture의 userScripts 선언은 사용자가 별도 게이트를 켜기 전까지는 비활성입니다. Thunderbit의 debugger 권한은 넓은 브라우저 제어 표면을 노출하지만, manifest만으로는 코드가 어떤 CDP 도메인이나 메서드를 호출하는지 알 수 없습니다. 각 행은 다음에 무엇을 확인해야 하는지 알려 줍니다. 런타임 네트워크 트래픽, 소스 코드, 동의 흐름, 브라우저 API 추적 중 하나입니다.

같은 분리는 Chrome 설치 프롬프트를 읽을 때도 중요합니다. 단순한 권한 개수로는 시점, 프레임 범위, OAuth scope, web_accessible_resources, 생략된 externally_connectable 키를 알 수 없습니다. 반대로 넓은 선언이 곧 수집이나 외부 유출의 증거도 아닙니다. manifest 감사의 유용한 산출물은 우선순위가 매겨진 런타임 테스트 계획입니다. 데이터 표면을 식별하고, 사용자 게이트를 확인한 뒤, 선언된 경로가 실제로 쓰이는지 관찰하세요.
자신의 manifest를 읽는 법
관련 리뷰: 브라우저 자동화 가이드.
chrome://extensions→ Details에서 부여된 사이트 접근 권한을 확인할 수 있고, 상시 접근이 필요 없는 항목은 on click으로 바꿀 수 있습니다. 이렇게 하면 위에서 말한 사이트 접근 범위의 절반을 줄일 수 있습니다. 하지만 수신 메시징(externally_connectable는 service worker로 그대로 들어옵니다)이나 브라우저 수준 권한에는 영향을 주지 않습니다. Thunderbit의debugger선언은 여기서 특히 중요합니다. Chromium의 보안 문서 자체에 따르면 debugger API는 경우에 따라 호스트 권한이나 파일 접근 같은 다른 일반적인 제한도 우회할 수 있다고 합니다. 따라서 사이트 접근을 “on click”으로 바꿔도, 그 확장 프로그램이 실제로 닿을 수 있는 범위가 곧바로 제한되는 건 아닙니다. 이 제어는tabs,webNavigation,clipboardRead,downloads, 또는optional_permissions에 포함된 항목에도 적용되지 않습니다.- 원본 파일은 Chrome의
Extensions/<id>/<version>/디렉터리 아래에 있습니다. 읽어야 할 필드는 6개입니다.permissions,optional_permissions,host_permissions,content_scripts(그 안의matches,run_at,all_frames),externally_connectable— 단, 키가 없을 때는 확장 프로그램 호출자에게 허용적이라는 점을 기억해야 합니다 — 그리고web_accessible_resources입니다. 이곳의matches는 콘텐츠 스크립트에는 없는 스킴을 적을 수 있습니다. Thunderbit의 마지막 필드에는index.html과 번들 스크립트 2개를 모든 출처에 노출하는<all_urls>항목이 2개 있고,use_dynamic_url: false이므로 페이지에서 이 안정적인 extension URL이 해석되는지 테스트할 수 있습니다. - 목록의 마지막 업데이트 날짜를 현재 Chrome 버전과 비교하세요.
공개 및 자료: 이 글은 Thunderbit가 발행했으며, 그 확장 프로그램도 같은 표와 파서에 포함되어 있습니다. 우리의 오픈소스 스크래퍼 피라미드에서는 확장 프로그램이 아닌 도구들도 다룹니다.
요약
스크래핑 및 자동화 확장 프로그램 10개, 우리 제품 포함, 모두 각자의 manifest를 읽어 보면 차이가 드러납니다.
Agenty는 사이트 접근도 콘텐츠 스크립트도 선언하지 않고, 클릭한 현재 탭에서만 동작합니다. 용량은 2.2 MiB입니다. Axiom.ai는 모든 HTTP 및 HTTPS URL을 선언하며, 용량은 37.1 MiB입니다. Magical은 clipboardRead를 포함한 13개 권한을 선언하며, 동시에 이 목록에서 수신 메시징을 완전히 닫는 유일한 확장 프로그램이기도 합니다. Table Capture는 권한이 4개뿐이지만, document_start에서 <all_urls>의 모든 프레임에 주입되며, 필요 시 userScripts를 요청할 수 있습니다. TexAu는 가장 작고, 가장 많은 대상 — 13개의 서로 다른 속성에 걸친 14개 패턴 — 을 명시합니다. 대상을 명시하는 것이 TexAu만의 특징은 아닙니다. Clip to Clay는 2개(clay.com과 한 개의 서드파티 사이트)를 선언하고, Hexomatic과 Clay for Chrome은 각자의 도메인을 적습니다. 다만 TexAu는 명시 목록의 대부분이 타사 사이트라는 점에서 독특합니다. 10개 중 5개는 디스크의 파일에 접근할 수 있으며, 그중 2개 — Axiom.ai와 우리 제품 — 은 file://를 한 번도 적지 않고도 접근합니다.
우리 제품 Thunderbit는 넓은 쪽에 있습니다. 호스트 접근과 정적 주입 모두 <all_urls>를 사용하고, 이 집합의 다른 어느 확장 프로그램도 선언하지 않는 debugger도 포함합니다. 메시징은 Chrome의 기본 허용 상태에 놓여 있고, Chrome의 사용자 게이트가 활성화되면 file://에도 도달할 수 있습니다.
선언은 상한이지 행동이 아니며, 이 모든 내용은 오용을 뜻하지 않습니다. 하지만 이건 공개되어 있고, 누구나 확인할 수 있으며, 양 끝의 차이는 제품 페이지에서 보이는 것보다 훨씬 큽니다.
Thunderbit로 웹 데이터 추출하기 Get Started Free
FAQ
광범위한 권한을 요청한다고 해서 확장 프로그램이 뭔가 잘못하고 있다는 뜻인가요? 그리고 왜 권한 개수만 세는 것으로는 부족한가요?
아닙니다. 그리고 개수만 세면 두 가지를 놓치기 때문입니다. <all_urls>는 확장 프로그램이 방문하는 모든 페이지를 읽을 수 있을지도 모른다는 뜻일 뿐, 실제로 무엇을 하는지나 데이터가 기기 밖으로 나가는지는 말해 주지 않습니다. 또 어떤 페이지에서든 데이터를 추출하려는 도구라면 이름이 정해진 도메인 목록만으로는 실제로 동작할 수 없습니다. 이번 결과의 핵심은 오용이 아니라 범위 차이이며, 실제 행동을 확인하려면 런타임 트래픽 분석이 필요한데 이번 감사에서는 하지 않았습니다. 개수만으로는 충분하지 않은 이유도 있습니다. 주입 설정은 권한이 아니기 때문입니다. Table Capture는 권한 4개만 선언하지만, 동시에 document_start, all_frames, <all_urls>에 주입합니다. 그리고 optional_permissions는 설치 프롬프트에 아예 나타나지 않습니다. Table Capture는 런타임에서 userScripts를 요청할 수 있고, 이를 통해 페이지 컨텍스트에서 임의의 사용자 스크립트를 실행할 수 있습니다. Magical은 webRequest를 요청할 수 있습니다.
이 중에서 가장 적게 요청하는 것은 무엇인가요?
Agenty입니다. 권한 4개, 호스트 권한 없음, 콘텐츠 스크립트 없음입니다. activeTab에 의존하는데, 이는 확장 프로그램을 클릭한 뒤 현재 탭에만, 그리고 다른 페이지로 이동하기 전까지만 접근을 허용합니다. 그다음은 Hexomatic으로, 권한이 2개뿐이고 콘텐츠 스크립트도 자체 도메인으로 제한됩니다.
manifest에 file://를 적지 않고도 로컬 파일에 어떻게 접근하나요?
<all_urls>가 file: 스킴을 포함하기 때문입니다. Listly, Magical, Table Capture는 file:// 패턴을 명시적으로 적습니다. Magical은 콘텐츠 스크립트에서, Table Capture는 web_accessible_resources에서 적습니다. Axiom.ai와 Thunderbit는 어디에도 따로 쓰지 않고도 와일드카드로 도달합니다. http://*/*와 https://*/*는 file://를 포함하지 않습니다. Chrome은 per-extension 토글 뒤에 파일 접근을 기본적으로 꺼 둡니다. 따라서 선언은 허가가 아니라 요청입니다.
externally_connectable는 무엇이고, 왜 생략이 더 허용적인 옵션인가요?
이 키는 어떤 웹 출처나 확장 프로그램 ID가 확장 프로그램의 백그라운드 스크립트에 메시지를 보낼 수 있는지를 정합니다. 키가 없을 때 Chrome의 기본값은 어떤 확장 프로그램은 연결할 수 있지만 어떤 웹페이지도 연결할 수 없다는 것입니다. 이 10개 중 7개가 이 키를 생략했고, Thunderbit도 여기에 포함됩니다. 키가 있으면 Hexomatic은 8개의 지정된 ID에 확장 프로그램 채널을 열고, Axiom.ai는 7개의 웹 출처를 열면서 확장 프로그램 채널은 닫아 둡니다. Magical은 두 채널 모두에 빈 리스트를 선언해, 둘 다 닫습니다.
Thunderbit 자체 manifest는 무엇을 선언하나요?
<all_urls>를 호스트 접근과 정적 콘텐츠 스크립트 주입에 사용하며, all_frames와 document_end로 동작합니다. 용량은 78개 파일에 걸쳐 15.9 MiB이고, 권한 배열에는 9개 문자열이 있습니다. 이 중 8개는 인식되는 권한입니다: activeTab, debugger, offscreen, scripting, sidePanel, storage, tabGroups, tabs. commands는 최상위 manifest 키에 속하며 그 배열 항목으로는 효과가 없습니다. 이 집합에서 debugger를 선언한 다른 확장 프로그램은 없습니다. Thunderbit는 Chrome의 파일 접근 게이트가 켜진 뒤 <all_urls>를 통해 file://에도 도달하며, externally_connectable는 생략합니다. 런타임 동작은 실행하지 않았기 때문에, 감사는 어떤 CDP나 scripting 메서드를 실제로 호출하는지는 주장하지 않습니다.


