Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

식탁보로 접속하기 (TableCloth Chrome Extension)

⚠️ 프리뷰 버전입니다. 아직 Chrome 웹 스토어에 등록되어 있지 않으며, 개발자 모드로 직접 불러와 사용합니다. 동작과 화면 구성이 예고 없이 바뀔 수 있습니다.

식탁보(TableCloth) 카탈로그에 등록된 은행과 공공 기관 사이트에 접속하면 페이지 상단에 "식탁보로 접속하기" 안내 막대를 띄우는 Chrome 확장(Manifest V3) 입니다. 안내 막대의 버튼을 누르면 다음 두 경로 중 하나로 이어집니다.

  • 식탁보 설치판이 있으면 tablecloth: 딥링크로 보고 있던 페이지를 그대로 샌드박스에서 엽니다. 이때는 파일을 만들지 않습니다.
  • 설치판이 없으면 무설치 식탁보용 .wsb 파일을 만들어 내려줍니다. 이 파일을 더블클릭하면 Windows Sandbox가 열리고, 그 안에서 해당 사이트가 미리 선택된 채로 식탁보가 실행됩니다. 호스트 PC에는 아무것도 설치되지 않습니다.

요구 사항

항목 조건
운영체제 Windows 10 또는 11의 Pro 이상 버전이어야 하고, Windows Sandbox 기능이 켜져 있어야 합니다.
브라우저 Chrome 108 이상이 필요합니다.
식탁보 (딥링크 경로) 정식 채널은 1.20.10 이상, 프리뷰 채널은 1.21.0-preview.2 이상이어야 합니다.
식탁보 (무설치 .wsb 경로) 따로 설치하지 않아도 됩니다. 샌드박스가 최신 포터블 빌드를 자동으로 받아 옵니다.

식탁보 버전 조건은 딥링크(tablecloth:) 경로에만 해당합니다. 이 버전부터 tablecloth: URI 스킴 등록과 대상 URL 게이트(CatalogTargetUrlMatcher)가 들어갔기 때문입니다. 더 낮은 버전을 쓰고 있다면 딥링크는 아무 반응이 없으므로, 안내 막대가 알려 주는 대로 무설치 .wsb 경로를 쓰면 됩니다.

근거로 삼은 규격

이 확장이 만들어 내는 결과물은 모두 식탁보 프로젝트가 정한 계약을 따릅니다.

항목 출처
.wsb 계약 (플레이스홀더 __SPORK_SITE_IDS__, 환경 변수 TABLECLOTH_SITE_IDS, ASCII 전용, 마운트 0개) docs/PARAMETERIZED_WSB_SPEC.md §0.5
tablecloth: 딥링크 계약 docs/DEEP_LINK_SCHEME.md
무설치 런처 동작 (고정 URL을 먼저 쓰고 실패하면 GitHub API로 넘어감) docs/EXPRESS_BOOTSTRAPPER_DESIGN.md
.wsb 원본 템플릿 tools/no-install/no-install-spork.wsb
카탈로그 스키마 Catalog.xsd
카탈로그 데이터 Catalog.xml

tools/verify.mjs는 이 확장이 만든 일반 런처 .wsbLogonCommand가 상류의 no-install-spork.wsb와 글자 단위로 같은지 대조합니다. 딥링크 변형은 규격 §0.5대로 $env:TABLECLOTH_SITE_IDS = ''<Id>''; 문장 하나만 덧붙입니다.

... SecurityProtocol -bor 3072; $env:TABLECLOTH_SITE_IDS = ''WooriBank''; try { iex ((New-Object ...

동작 방식

카탈로그 XML ──▶ 스냅샷(JSON) ──▶ 호스트, 등록가능도메인 인덱스
                                        │
페이지 접속 ────────────────────────────┴─▶ 일치? ──▶ 상단 안내 막대
                                                        │ 클릭
                          ┌─────────────────────────────┘
                          │
              ① tablecloth:<보던 URL>  ──▶ 설치판 식탁보가 그 페이지로 진입 (파일 없음)
                          │
                          │ 창이 열리지 않았다고 사용자가 알려 줌
                          ▼
              ② .wsb 생성 ──▶ 다운로드 ──▶ 더블클릭 ──▶ Windows Sandbox ──▶ 대표 URL로 진입

두 경로의 차이는 설치판이 있는지 여부와, 어느 주소로 진입하는지입니다. ①은 파일을 만들지 않고 보고 있던 페이지를 그대로 열며, ②는 .wsb를 받아 카탈로그의 대표 URL로 진입합니다.

  • 도메인을 판정하는 방법. 카탈로그 Service/@Url의 호스트와 정확히 비교하고, 일치하지 않으면 등록가능도메인(eTLD+1)으로 다시 비교합니다. 그래서 www.wooribank.com이 등록되어 있으면 pib.wooribank.com에서도 안내 막대가 뜹니다. .kr의 2단계 접미사(co.kr, or.kr, go.kr 등)를 모두 알고 있으므로 서로 무관한 co.kr 사이트끼리 잘못 묶이지 않습니다. 정확히 일치할 때만 뜨게 하려면 설정에서 끌 수 있습니다.
  • 카탈로그를 갱신하는 방법. 12시간마다 공식 카탈로그를 다시 받아 옵니다. 네트워크가 없거나 처음 실행하는 경우에는 확장에 포함된 스냅샷(data/catalog-snapshot.json)을 씁니다.
  • .wsb를 만드는 방법. 모든 처리를 확장 안에서 합니다. 사이트 Id는 카탈로그 화이트리스트에서만 오며, ^[A-Za-z0-9][A-Za-z0-9._-]*$를 벗어나면 생성 자체를 거부합니다. PowerShell 인용부호 주입을 막기 위한 장치입니다.
  • 딥링크를 만드는 방법. 보고 있던 URL이 게스트의 도메인 게이트를 통과할 것으로 보이면 URL 형태로 보내고, 그렇지 않으면 사이트 Id 형태로 낮춰 보냅니다. 자세한 내용은 아래에서 설명합니다.

tablecloth: 딥링크와 게이트 예측

딥링크는 다음 두 형태를 지원하며, 하나의 딥링크에 둘을 함께 실을 수는 없습니다.

형태 진입 주소
대상 URL tablecloth:https://spib.wooribank.com/pib/Dream?... 지정한 그 주소로 진입합니다.
사이트 Id tablecloth:WooriBank 카탈로그의 대표 URL로 진입합니다.

대상 URL은 게스트의 CatalogTargetUrlMatcher가 다시 검사하며, 통과하지 못하면 조용히 버려집니다. 그렇게 되면 사용자는 왜 엉뚱한 페이지가 열렸는지 알 수 없습니다. 그래서 src/common/deeplink.js가 그 판정 규칙을 그대로 옮겨 미리 예측하고, 버려질 URL이라면 애초에 사이트 Id 형태로 낮춰 보냅니다.

npm run verify는 상류 문서 §6의 예시를 그대로 재현하는지 확인합니다.

입력 판정
https://www.wooribank.com/ 후보가 하나뿐이므로 WooriBank로 확정합니다.
https://spib.wooribank.com/… 동점이므로 카탈로그에 먼저 적힌 WooriBank로 확정합니다.
https://ok.ibs.fsb.or.kr/ 라벨이 3개 일치하므로 OKSavingsBank로 확정합니다.
https://www.fsb.or.kr/ 동점인데 서비스가 25개이고 서로 다른 회사이므로 URL을 버립니다.
https://evilwooribank.com/ 라벨 단위로 비교하므로 통과하지 못합니다.
https://www.wooribank.com@evil.example/ 자격 증명이 들어 있으므로 버립니다.

상류 구현이 바뀌면 이 예측기도 같이 고쳐야 합니다. 두 판정이 어긋나면 URL이 조용히 버려집니다.

설치 여부는 감지할 수 없습니다

브라우저는 커스텀 스킴 핸들러가 등록되어 있는지를 스크립트에 알려 주지 않습니다. 확장 API로도 마찬가지 라는 점을 실제로 측정해 확인했습니다. 등록된 tablecloth:와 확실히 존재하지 않는 스킴이 완전히 같은 이벤트를 냅니다. 양쪽 모두 tabs.update가 성공하고, webNavigation.onErrorOccurrednet::ERR_ABORTED로 뜨며, 탭은 원래 페이지에 그대로 남습니다.

포커스가 빠지는 것을 이용하는 방법도 쓸 수 없습니다. 그 방법은 설치되지 않은 경우와 사용자가 확인창을 취소한 경우를 구분하지 못합니다. 취소한 사용자에게 원하지도 않은 .wsb가 자동으로 내려가는 쪽이 더 나쁘기 때문에 쓰지 않았습니다.

그래서 추측하지 않고 순차형으로 처리합니다. 기본 버튼이 먼저 설치판을 시도하고, 곧바로 창이 열리지 않았을 때 무설치 .wsb를 받는 선택지를 함께 보여 줍니다. 사용자가 .wsb 경로를 한 번 쓰면 preferNoInstall 설정이 켜져서 다음부터는 그 선택이 기본이 됩니다. 설정 페이지에서 되돌릴 수 있습니다.

설치 (개발자 모드)

웹 스토어에 등록하기 전이라 압축을 푼 상태로 불러와야 합니다. 별도의 빌드 단계는 없으므로 내려받은 폴더를 그대로 지정하면 됩니다.

1. 소스 받기

git clone https://github.com/yourtablecloth/TableClothChrome.git

ZIP 파일로 받았다면 압축을 풀어 두십시오. 확장을 쓰는 동안 이 폴더가 그대로 남아 있어야 합니다. Chrome은 파일을 복사해 두지 않고 이 경로를 계속 참조하므로, 폴더를 옮기거나 지우면 확장도 사라집니다.

2. Chrome에 불러오기

  1. 주소창에 chrome://extensions를 입력해 확장 프로그램 페이지를 엽니다.
  2. 오른쪽 위에 있는 개발자 모드 토글을 켭니다.
  3. 왼쪽 위에 나타나는 압축해제된 확장 프로그램을 로드를 누릅니다.
  4. 1단계에서 받은 폴더를 선택합니다. manifest.json이 직접 들어 있는 폴더여야 합니다.

src 폴더나 그 상위 폴더가 아니라 manifest.json이 있는 폴더를 골라야 합니다.

3. 확인하기

카탈로그에 있는 사이트에 접속해 봅니다. 예를 들어 https://www.wooribank.com/에 접속하면 페이지 상단에 안내 막대가 뜹니다. 툴바의 식탁보 아이콘을 누르면 현재 사이트의 상태와 카탈로그 검색을 볼 수 있습니다.

아이콘이 툴바에 보이지 않으면 퍼즐 조각 모양의 확장 프로그램 버튼을 눌러 목록에서 고정하십시오.

4. 갱신하기

git pull

내려받은 다음 chrome://extensions에서 이 확장의 새로고침 버튼을 누르면 반영됩니다. 이미 열려 있던 탭은 새로 고쳐야 안내 막대가 새 코드로 다시 뜹니다.

문제가 생기면

증상 확인할 것
안내 막대가 뜨지 않습니다. 그 사이트가 카탈로그에 있는지 툴바 팝업에서 검색해 봅니다. 설정에서 안내 막대를 껐는지, 그 사이트에서 "이 사이트에서 그만 보기"를 누른 적이 있는지도 확인합니다.
안내 막대는 뜨는데 딥링크가 반응하지 않습니다. 식탁보 버전이 위 요구 사항을 만족하는지 확인합니다. 버전이 낮다면 .wsb 경로를 쓰면 됩니다.
.wsb.xml로 저장됩니다. 이미 해결된 문제이므로 확장을 최신으로 갱신합니다. 탐색기에서 파일 확장명 표시를 켜고 실제 이름을 확인하십시오.
오류를 자세히 보고 싶습니다. chrome://extensions에서 이 확장의 서비스 워커 링크를 눌러 콘솔을 엽니다.

개발

npm run verify         # 도메인 매칭, 딥링크 게이트, .wsb 생성을 브라우저 없이 점검합니다
npm run catalog        # 공식 카탈로그를 다시 받아 스냅샷과 manifest matches를 갱신합니다
npm run catalog:check  # 갱신하지 않고 차이만 확인합니다 (차이가 있으면 종료 코드 1)

상류 원본과의 대조까지 하려면 다음과 같이 실행합니다.

node tools/verify.mjs --upstream <TableCloth 저장소>/tools/no-install/no-install-spork.wsb

카탈로그가 늘어나면

npm run catalogmanifest.jsoncontent_scripts[0].matches를 카탈로그의 등록가능도메인 목록으로 다시 씁니다. <all_urls> 대신 실제 등록된 도메인만 선언해 권한 요구를 최소화하려는 설계이므로, 카탈로그에 사이트가 추가되면 이 스크립트를 다시 돌려 재배포해야 정적 매치가 따라갑니다.

재배포하기 전이라도 확장은 런타임에 카탈로그를 갱신하며, 정적 매치에 없는 도메인을 팝업과 설정 페이지에서 알려 줍니다. 사용자가 선택적 권한을 허용하면 chrome.scripting.registerContentScripts로 동적 등록해 그 사이트에도 안내 막대가 뜨게 합니다.

구성

manifest.json                 MV3 매니페스트 (matches는 빌드 스크립트가 생성)
data/catalog-snapshot.json    카탈로그 스냅샷 (오프라인, 첫 실행용)
src/common/psl.js             .kr 인식 공개 접미사 처리, 등록가능도메인 계산
src/common/xml.js             시작 태그 특성만 훑는 최소 XML 스캐너
src/common/catalog.js         Catalog.xml 파싱, 호스트와 도메인 인덱스, 매칭
src/common/wsb.js             .wsb 생성, 사이트 Id 검증, data URL 변환
src/common/deeplink.js        tablecloth 딥링크와 게스트 도메인 게이트 예측기
src/common/storage.js         설정, 숨김 기록, 카탈로그 캐시
src/common/constants.js       URL, 저장소 키, 메시지 타입, 기본 설정
src/background/service-worker.js  카탈로그 갱신, 매칭, 다운로드, 동적 스크립트 등록
src/content/banner.js         상단 안내 막대 (섀도 DOM, 페이지 CSP 비의존)
src/popup/                    툴바 팝업 (현재 사이트, 카탈로그 검색)
src/options/                  설정 페이지
icons/                        확장 아이콘 (공식 로고에서 생성)
tools/build-catalog.mjs       카탈로그를 스냅샷과 manifest matches로 변환
tools/build-icons.ps1         공식 로고를 아이콘 4종과 안내 막대용 data URL로 변환
tools/pack.mjs                스토어 패키지에 넣을 파일 목록 정의와 점검
tools/verify.mjs              자체 점검

서비스 워커에는 DOMParser가 없어서 XML 스캐너를 직접 만들어 넣었습니다.

아이콘

아이콘은 상류의 docs/images/TableCloth_NewLogo.png 에서 생성합니다. 원본은 2160×2160 크기이지만 투명 여백이 가로로 30%, 세로로 51%를 차지합니다. 그대로 축소하면 16px에서 형체를 알아볼 수 없으므로, 알파 경계 상자로 잘라낸 다음 4% 여백을 두고 다시 그립니다.

pwsh tools/build-icons.ps1              # icons/icon-{16,32,48,128}.png를 다시 만듭니다
pwsh tools/build-icons.ps1 -EmitBase64  # 안내 막대용 data URL도 함께 출력합니다

안내 막대는 콘텐츠 스크립트에서 동작하므로 로고를 src/content/banner.jsMARK_DATA_URL에 인라인해 씁니다. chrome.runtime.getURL로 확장 리소스를 가리키면 이미지를 불러오는 주체가 페이지가 되어 web_accessible_resources 선언이 필요해지는데, 아이콘 하나 때문에 확장 리소스를 모든 사이트에 노출할 이유가 없기 때문입니다. 사이트 CSP가 data: 이미지를 막는 경우에는 로고만 조용히 빠집니다.

권한

권한 쓰임
storage 카탈로그 캐시와 설정, 숨김 기록을 저장합니다.
downloads 만들어 낸 .wsb를 저장하고 다운로드 폴더에서 보여 줍니다.
alarms 12시간마다 카탈로그를 갱신합니다.
scripting 카탈로그에 새로 추가된 도메인에 콘텐츠 스크립트를 동적으로 등록합니다.
activeTab 팝업에서 현재 탭의 주소를 확인합니다.
https://yourtablecloth.app/* 카탈로그 XML을 내려받습니다.
content_scripts.matches 카탈로그에 등록된 도메인에서만 안내 막대를 띄웁니다.
optional_host_permissions 사용자가 명시적으로 허용할 때만 새 도메인으로 범위를 넓힙니다.

tabs 권한은 쓰지 않습니다. 배지는 안내 막대가 자기 탭을 알려 주는 방식으로 표시합니다.

알아둘 점

  • Windows 10 또는 11의 Pro 이상 버전에서 Windows Sandbox 기능이 켜져 있어야 합니다. Windows가 아닌 환경에서는 기본적으로 안내 막대를 띄우지 않으며, macOS에서 MacSandbox를 쓰는 경우에만 설정에서 켜면 됩니다.
  • 샌드박스에 호스트 폴더를 연결하지 않으므로 파일 형태의 공동인증서는 사용할 수 없습니다. 모바일 인증을 쓰십시오.
  • 브라우저가 .wsb 다운로드를 낯선 형식이라며 경고할 수 있습니다. 이 확장이 만드는 파일은 공식 GitHub 릴리스만 가리키고 내용도 XML 평문이므로, 열어서 직접 확인할 수 있습니다.
  • chrome.downloadsdata: URL 저장을 거부하는 환경에서는 안내 막대와 팝업이 페이지 컨텍스트에서 blob으로 저장하도록 자동으로 넘어갑니다.

다운로드 MIME 타입을 옥텟 스트림으로 고정한 이유

Chrome은 다운로드 파일 이름의 확장자를 MIME 타입의 기본 확장자로 덮어씁니다. .wsb에는 등록된 MIME 타입이 없어서, 내용에 맞는 application/xml을 쓰면 지정한 이름이 무시되고 .xml로 저장됩니다. 그러면 더블클릭해도 Windows Sandbox가 뜨지 않습니다. 다음은 Chrome for Testing 151에서 실제로 측정한 결과이며, 요청한 이름은 모두 *.wsb였습니다.

data: URL의 MIME 실제 저장된 이름
application/xml probe-A-xml.xml
text/plain probe-C-plain.txt
MIME을 지정하지 않음 probe-D-nomime.txt
application/octet-stream probe-B-octet.wsb

그래서 WSB_MIME_TYPEapplication/octet-stream으로 고정했고, npm run verify가 이를 회귀 테스트로 지킵니다. 덧붙여 서비스 워커는 저장이 끝난 뒤 실제 파일 이름을 다시 조회해서, 그래도 확장자가 바뀌었다면 안내 막대에서 사용자에게 알려 줍니다.

탐색기에서 "알려진 파일 형식의 확장명 숨기기"가 켜져 있으면 이름.wsb.xml이름.wsb로 보입니다. 여기에 .wsb를 다시 붙여 이름을 고쳐도 실제 이름은 이름.wsb.xml 그대로여서 실행되지 않습니다.

배포

manifest.json의 버전을 올리고 같은 번호로 태그를 붙여 올리면, GitHub Actions가 패키지를 만들어 릴리스로 발행합니다. 스토어 시크릿이 설정되어 있으면 Chrome 웹 스토어와 Microsoft Edge 애드온에도 제출합니다. 자세한 절차와 시크릿 설정 방법은 docs/PUBLISHING.md에 있습니다.

git tag v0.2.0
git push origin main --tags

라이선스

이 프로젝트는 상류 식탁보 프로젝트와 같은 AGPL-3.0-or-later를 따릅니다. 전문은 LICENSE 파일에 있습니다.

브라우저 확장에 AGPL을 적용해도 문제가 없는지 검토했고, 다음과 같은 이유로 그대로 두기로 했습니다.

  • AGPL을 GPL과 구분 짓는 제13조는 네트워크 너머로 프로그램과 상호작용하는 사용자에게 소스를 제공하도록 요구하는 조항입니다. 이 확장은 사용자의 브라우저 안에서만 동작하고 서버가 없으므로 그 조항이 실제로 발동할 일이 없습니다. 결과적으로 GPL-3.0과 같게 동작합니다.
  • 스토어 약관과의 충돌은 저작권을 전부 보유한 경우에는 생기지 않습니다. 과거 App Store와 GPL 사이의 분쟁은 배포자가 모든 기여분의 저작권을 갖고 있지 않았고 스토어가 추가 제약을 걸었기 때문에 생긴 일입니다. Chrome 웹 스토어에는 GPL 계열 확장이 이미 여럿 올라가 있습니다.
  • 이 확장은 빌드 단계가 없어서 스토어에 올리는 패키지가 곧 소스 코드입니다. 그래서 소스 제공 의무가 자연스럽게 충족되며, 패키지에 LICENSE를 함께 넣어 제4조 요구도 만족합니다.

다만 AGPL은 기업 정책에서 배제되는 경우가 있습니다. 이 확장이 사내 배포를 가로막는 걸림돌이 된다면 다음을 대안으로 검토할 수 있습니다. GPL-3.0-or-later는 실제로 발동하지 않는 제13조를 걷어내면서 같은 카피레프트를 유지합니다. MPL-2.0은 파일 단위 카피레프트여서 다른 프로젝트에 포함되기 쉽습니다. Apache-2.0은 채택 장벽이 가장 낮지만 이중 라이선스 전략의 지렛대를 잃습니다.

About

TableCloth Extension for Chromium Browsers

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages