Prevent IROS download path failures after payment

The IROS docs now make the corp-number happy path produce the company-name list that pinned upstream iros_download.py opens after payment, and route the customer workbook excel_path into the same private workdir boundary as other sensitive inputs and outputs.

Constraint: Live IROS login/payment smoke requires user credentials, certificate/authentication, and card payment authority

Rejected: Rely on upstream data/ defaults | leaves real customer workbook and company list paths inside the cloned repository

Confidence: high

Scope-risk: narrow

Tested: node --test --test-name-pattern='iros-registry-automation' scripts/skill-docs.test.js

Tested: ./scripts/validate-skills.sh

Tested: npm run lint && npm run typecheck && npm test

Tested: npm run ci

Tested: cloned pinned upstream SHA and verified companies_list, excel_path, and configured paths resolve under private temp workdir

Not-tested: Live IROS login/payment smoke; requires user credentials/authentication/payment authority
This commit is contained in:
Jeffrey (Dongkyu) Kim 2026-04-28 14:47:30 +09:00
commit c0b38fc517
3 changed files with 62 additions and 8 deletions

View file

@ -46,10 +46,10 @@ Chrome/Chromium, Python 3.10+, IROS 로그인 수단, 결제 카드, TouchEn nxK
```bash
workdir="$(mktemp -d "${TMPDIR:-/tmp}/iros-registry.XXXXXX")"
chmod 700 "$workdir"
mkdir -p "$workdir/downloads" "$workdir/logs" "$workdir/output"
mkdir -p "$workdir/downloads" "$workdir/logs" "$workdir/output" "$workdir/tmp-downloads"
```
실제 입력은 upstream repo `data/`가 아니라 `$workdir/corp-input.json`, `$workdir/realty-input.json`처럼 저장소 밖에 둔다. upstream `data/` 디렉터리는 샘플 형식 확인용으로만 보고, 실제 법인등록번호·주소·동호수 원문을 넣지 않는다.
실제 입력은 upstream repo `data/`가 아니라 `$workdir/corp-input.json`, `$workdir/realty-input.json`, `$workdir/customer-list.xlsx`처럼 저장소 밖에 둔다. upstream `data/` 디렉터리는 샘플 형식 확인용으로만 보고, 실제 법인등록번호·주소·동호수·고객 Excel 원문을 넣지 않는다.
```bash
cat > "$workdir/corp-input.json" <<'JSON'
@ -58,8 +58,23 @@ cat > "$workdir/corp-input.json" <<'JSON'
"1101117654321": "샘플 주식회사"
}
JSON
python3 - "$workdir" <<'PY'
import json
import pathlib
import sys
workdir = pathlib.Path(sys.argv[1])
corp_input = json.loads((workdir / "corp-input.json").read_text())
companies = list(corp_input.values())
(workdir / "companies-input.json").write_text(
json.dumps(companies, ensure_ascii=False, indent=2) + "\n"
)
PY
```
`iros_download.py`는 결제 후 열람·저장 단계에서 `companies_list`를 열어 저장 파일명을 맞춘다. 법인등록번호 기반 `iros_cart_by_corpnum.py`를 쓰더라도 결제 전에 `$workdir/companies-input.json`을 준비해야 결제 후 다운로드가 로컬 `FileNotFoundError` 없이 이어진다.
`config.json`의 입력·로그·save_dir 관련 값을 `$workdir`로 돌리면 upstream 스크립트를 실행해도 저장소 하위 `data/`, `logs/`, `output/`에 실제 업무 정보가 남지 않는다.
```bash
@ -74,6 +89,7 @@ config.update({
"corpnum_list": str(workdir / "corp-input.json"),
"companies_list": str(workdir / "companies-input.json"),
"realty_list": str(workdir / "realty-input.json"),
"excel_path": str(workdir / "customer-list.xlsx"),
"save_dir": str(workdir / "downloads"),
"realty_save_dir": str(workdir / "downloads" / "realty"),
"pdf_dir": str(workdir / "downloads"),
@ -106,7 +122,7 @@ python iros_cart_by_corpnum.py
python iros_download.py
```
위 명령은 로컬 `config.json`을 읽으므로, 먼저 `corpnum_list``save_dir``$workdir/corp-input.json`, `$workdir/downloads`를 가리키는지 확인한다.
위 명령은 로컬 `config.json`을 읽으므로, 먼저 `corpnum_list`, `companies_list`, `save_dir`가 각각 `$workdir/corp-input.json`, `$workdir/companies-input.json`, `$workdir/downloads`를 가리키는지 확인한다.
## 부동산등기부등본 흐름
@ -129,7 +145,7 @@ python iros_cart_realty.py
python iros_wizard.py
```
마법사는 법인/부동산 장바구니, 결제 후 열람·저장, 사업자번호 기반 법인정보 조회, 다운로드된 법인 PDF 종합 리포트 생성을 메뉴로 제공한다.
마법사는 법인/부동산 장바구니, 결제 후 열람·저장, 사업자번호 기반 법인정보 조회, 다운로드된 법인 PDF 종합 리포트 생성을 메뉴로 제공한다. 사업자번호/고객 workbook 경로는 `excel_path``$workdir/customer-list.xlsx`를 가리키게 한 뒤 사용하고, upstream repo `data/고객리스트.xlsx`에는 실제 고객 Excel을 두지 않는다.
## 트러블슈팅

View file

@ -60,11 +60,13 @@ cp config.json.example config.json
```bash
workdir="$(mktemp -d "${TMPDIR:-/tmp}/iros-registry.XXXXXX")"
chmod 700 "$workdir"
mkdir -p "$workdir/downloads" "$workdir/logs" "$workdir/output"
mkdir -p "$workdir/downloads" "$workdir/logs" "$workdir/output" "$workdir/tmp-downloads"
```
법인등록번호 기반 입력은 upstream repo `data/`가 아니라 `$workdir/corp-input.json` 같은 저장소 밖 파일에 둔다. 실제 법인등록번호/주소 원문을 upstream `data/` 디렉터리, git 저장소, PR 첨부, 테스트 로그에 넣지 않는다.
`iros_download.py`는 결제 후 열람·저장 단계에서 `companies_list`를 열어 저장 파일명을 맞춘다. 법인등록번호 흐름을 쓰더라도 결제 전 `$workdir/companies-input.json`을 함께 만들어 둔다.
```bash
cat > "$workdir/corp-input.json" <<'JSON'
{
@ -72,10 +74,25 @@ cat > "$workdir/corp-input.json" <<'JSON'
"1101117654321": "샘플 주식회사"
}
JSON
python3 - "$workdir" <<'PY'
import json
import pathlib
import sys
workdir = pathlib.Path(sys.argv[1])
corp_input = json.loads((workdir / "corp-input.json").read_text())
companies = list(corp_input.values())
(workdir / "companies-input.json").write_text(
json.dumps(companies, ensure_ascii=False, indent=2) + "\n"
)
PY
```
부동산 주소 기반 입력 예시는 동/호수까지 필요한 경우가 있으므로 `data/iros_realties.json` 형식을 upstream README에서 확인하되, 실제 주소 원문은 `$workdir/realty-input.json` 같은 로컬 파일에만 둔다.
사업자번호 조회나 종합 리포트 마법사 흐름에서 쓰는 고객 Excel도 upstream repo `data/`가 아니라 `$workdir/customer-list.xlsx` 같은 저장소 밖 파일에 둔다. 실제 고객 목록을 upstream `data/고객리스트.xlsx`에 복사하지 않는다.
`config.json`도 저장소에 커밋하지 않는 로컬 파일로 두고, 민감 입력·로그·산출물 경로를 모두 `$workdir` 아래로 돌린다.
```bash
@ -90,6 +107,7 @@ config.update({
"corpnum_list": str(workdir / "corp-input.json"),
"companies_list": str(workdir / "companies-input.json"),
"realty_list": str(workdir / "realty-input.json"),
"excel_path": str(workdir / "customer-list.xlsx"),
"save_dir": str(workdir / "downloads"),
"realty_save_dir": str(workdir / "downloads" / "realty"),
"pdf_dir": str(workdir / "downloads"),
@ -135,7 +153,7 @@ python iros_cart.py
python iros_download.py
```
저장 경로는 `config.json``save_dir`로 관리하되, 위 예시처럼 `$workdir/downloads`를 사용하고 공개 저장소 하위 경로를 사용하지 않는다.
저장 경로는 `config.json``save_dir`로 관리하되, 위 예시처럼 `$workdir/downloads`를 사용하고 공개 저장소 하위 경로를 사용하지 않는다. `companies_list``$workdir/companies-input.json`을 가리키는지 결제 전에 확인하면 결제 후 `iros_download.py``FileNotFoundError`로 중단되는 일을 피할 수 있다.
### 5. 부동산등기부등본 장바구니 담기
@ -161,8 +179,8 @@ python iros_wizard.py
- 법인등기부등본 — 결제 후 열람·저장
- 부동산등기부등본 — 장바구니 담기
- 부동산등기부등본 — 결제 후 열람·저장
- 사업자번호 → 법인정보 조회
- 다운로드된 법인 PDF → 종합 리포트 엑셀 생성
- 사업자번호 → 법인정보 조회 (`excel_path``$workdir/customer-list.xlsx`)
- 다운로드된 법인 PDF → 종합 리포트 엑셀 생성 (`excel_path``pdf_dir`는 저장소 밖 경로)
## Response policy

View file

@ -3066,6 +3066,16 @@ test("iros-registry-automation skill documents safe IROS registry certificate au
assert.match(skill, /scripts\/upstream\.pin/, "skill should document where the reviewed upstream pin lives");
assert.match(skill, /pin update|핀 업데이트|업스트림 핀|review/i, "skill should require review before updating the pin");
assert.match(skill, /\$workdir\/corp-input\.json/, "skill should put real corporate inputs under the private workdir");
assert.match(
skill,
/\$workdir\/companies-input\.json[\s\S]*(iros_download\.py|결제 후|열람|저장)/,
"skill should prepare the company-name list required by iros_download.py before the download flow after payment",
);
assert.match(
skill,
/"excel_path":\s*str\(workdir \/ "customer-list\.xlsx"\)/,
"skill should redirect upstream customer Excel input to the private workdir",
);
assert.match(skill, /\$workdir\/downloads/, "skill should point save_dir/download output at the private workdir");
assert.match(skill, /config\.json[\s\S]*save_dir[\s\S]*\$workdir/, "skill should wire config.json save_dir to the private workdir");
assert.match(skill, /upstream repo `data\/`|upstream `data\/`|data\/.*실제/, "skill should warn not to use upstream data/ for real inputs");
@ -3089,6 +3099,16 @@ test("iros-registry-automation skill documents safe IROS registry certificate au
assert.match(featureDoc, /i?ros_cart_realty\.py|부동산 장바구니/);
assert.match(featureDoc, /저장소 밖|레포 밖|커밋하지/);
assert.match(featureDoc, /\$workdir\/corp-input\.json/, "feature doc should put real corporate inputs under the private workdir");
assert.match(
featureDoc,
/\$workdir\/companies-input\.json[\s\S]*(iros_download\.py|결제 후|열람|저장)/,
"feature doc should prepare the company-name list required by iros_download.py before the download flow after payment",
);
assert.match(
featureDoc,
/"excel_path":\s*str\(workdir \/ "customer-list\.xlsx"\)/,
"feature doc should redirect upstream customer Excel input to the private workdir",
);
assert.match(featureDoc, /\$workdir\/downloads/, "feature doc should point save_dir/download output at the private workdir");
assert.match(featureDoc, /config\.json[\s\S]*save_dir[\s\S]*\$workdir/, "feature doc should wire config.json save_dir to the private workdir");
assert.match(featureDoc, /upstream repo `data\/`|upstream `data\/`|data\/.*실제/, "feature doc should warn not to use upstream data/ for real inputs");