전체 문서
이 페이지 목차
자산·scope·coverage 흐름
ARTEX는 전역 자산 graph, 회사 귀속 범위, 태스크별 자산 연결, 태스크 scope, 탐색 node의 asset anchor를 분리한다. 같은 domain이 한 번만 upsert돼도 여러 task가 이를 참조할 수 있고, “자산이 존재함”, “이 task에서 볼 수 있음”, “테스트를 허용함”, “coverage 분모에 포함함”은 서로 다른 상태다. Asset store
Mermaid 원문
flowchart LR Input[UI · Agent · ScopeSentry] --> Normalize[유형별 정규화] Normalize --> Gate[Global block + Task block/allow] Gate -->|거부| Reject[오류·삽입/intent 중단] Gate -->|허용| Assets[(assets)] Assets --> Link[(task_asset_links / task_ids)] Assets --> Company[(companies + company_scope)] Assets --> Anchor[(exploration_anchors)] Link --> Scope[(task_scope)] Scope --> Coverage[coverage denominator] Anchor --> Intent[worker intent] Dormant[조립됐지만 호출되지 않는\nDNS/HTTP enrichment] -.-> Assets click Gate "../security-boundaries.md#asset-gate" "Asset gate" click Assets "../contracts/db/task-exploration.md#table-assets" "assets table" click Scope "../contracts/db/task-exploration.md#table-task-scope" "task_scope" click Coverage "#coverage" "Coverage 계산" click Dormant "#enrichment" "연결되지 않은 보강 경로"
자산 유형과 정체성
| 유형 | 주요 식별 입력 | dedup/연결 의미 |
|---|---|---|
root_domain |
정규화 domain | domain unique partial index |
subdomain |
domain + optional record type/value | domain/record type 조합으로 합쳐지고 DNS 값은 병합될 수 있음 |
ip |
유효 IPv4/IPv6 | IP unique; bound domains/open ports는 자산 속성 |
app |
bundle id 또는 app name | bundle id 우선, 없으면 name 기준 partial unique |
service HTTP |
정규화 URL | URL unique; status/title/technology/auth와 host/IP 연결 |
service other |
domain/IP + port + service/protocol | HTTP와 다른 composite identity |
endpoint |
URL + method | parameter 관찰을 합치며 서비스/host와 논리 연결 |
assets row는 JSON/array 속성과 typed columns를 함께 쓴다. DB partial unique index는 물리 중복의 일부를 막지만 URL/domain normalization과 merge 정책은 AssetStore 코드가 소유한다. 같은 root cause나 동일 의미 endpoint의 semantic dedup을 보장하지 않는다. 자산 schema
자산이 들어오는 경로
자산이 들어오는 경로 · 관계
이 대상이 사용하는 구현·계약·의존 · 2개
- 계약 사용 · companies · DB.SearchChatMentionsPage: read FROM; CompanyStore.CreateCompanyWithScope: write INSERT; CompanyStore.DeleteCompanyWithAssets: write DELETE; CompanyStore.GetCompany: read FROM; CompanyStore.GetCompanyByName: read FROM; CompanyStore.ListCompanies: read FROM; CompanyStore.UpsertCompany: write INSERT; ensureCompanyExistsTx: read FROM; AssetStore.ListTaskScopeWithSources: read JOIN; AssetStore.BuildCoverageGraph: read FROM; AssetStore.ListTaskScope: read JOIN; insertTaskCompanies: read JOIN · db-pg-access-companies-027ee36311 · db-pg-access-companies-8a08847bd5 · db-pg-access-companies-5d7af36dbc · db-pg-access-companies-82f2f97b5f · db-pg-access-companies-5a99aca97f · db-pg-access-companies-9c33327a89 · db-pg-access-companies-5558ff7874 · db-pg-access-companies-1c607bde47 · db-pg-access-companies-e2feb4cf84 · db-pg-access-companies-26853e3459 · db-pg-access-companies-83c7ae4ebd · db-pg-access-companies-592d9370e6
- 구현 담당 · PostgreSQL 책임 영역 · 기능 단계의 직접 상태/실행 구현 · feature-impl-6
| 경로 | 입력→결과 | 실패·완료 경계 |
|---|---|---|
Agent insert_assets |
혼합 배열을 유형별 validate/upsert, task·intent provenance 연결 | item별 partial success; results와 errors를 함께 반환 |
UI/API /api/assets |
사용자 입력을 server handler가 upsert | 응답은 DB mutation 기준 |
| Task asset attach/scope | 기존 asset 연결 또는 domain/IP scope에서 asset 생성·연결 | 한 요청의 validation/transaction 규칙은 db/task_assets.go가 소유 |
| ScopeSentry sync | remote project/task asset을 가져와 ARTEX type으로 변환·upsert | remote API 성공과 local insert 결과를 구분 |
| Company scope | domain/IP/CIDR/ICP/keyword 규칙을 저장하고 기존 asset 귀속 재계산 | keyword는 agent 힌트이며 모든 종류가 자동 귀속 key는 아님 |
| Enrichment engine | manager가 engine을 만들고 worker tool set에 주입 | 현재 production 코드에는 ResolveDomain/ProbeSite 호출자가 없어 자산 write 뒤 자동 실행되지 않음 |
ScopeSentry credential과 datasource는 settings에 저장되고 sync handler가 외부 API를 호출한다. 이 문서 작성에서는 원격 service를 실행하지 않았다. ScopeSentry
Agent write와 provenance
Agent write와 provenance · 관계
이 대상이 사용하는 구현·계약·의존 · 3개
- 계약 사용 · assets · AssetStore.CountDSL: read FROM; AssetStore.GetByIDsInScope: read FROM; AssetStore.QueryDSL: read FROM; AssetStore.QueryDSLInScope: read FROM; AssetStore.AppendIPBoundDomain: write UPDATE; AssetStore.AppendIPPort: write UPDATE; AssetStore.CountByCompany: read FROM; AssetStore.CountByTask: read FROM; AssetStore.CountByType: read FROM; AssetStore.CountsByType: read FROM; AssetStore.CountsByTypeForTask: read FROM; AssetStore.DeleteByCompanyID: write DELETE; AssetStore.DeleteByHost: write DELETE; AssetStore.DeleteByIDs: write DELETE; AssetStore.DeleteByTaskID: write DELETE/UPDATE; AssetStore.GetByIDs: read FROM; AssetStore.HostsByTask: read FROM; AssetStore.QueryByCompany: read FROM; AssetStore.QueryByTask: read FROM; AssetStore.QueryByType: read FROM; AssetStore.UpsertApp: write INSERT; AssetStore.UpsertEndpoint: write INSERT; AssetStore.UpsertHTTPService: write INSERT; AssetStore.UpsertIP: write INSERT; AssetStore.UpsertOtherService: write INSERT; AssetStore.UpsertRootDomain: write INSERT; AssetStore.UpsertSubdomain: write INSERT; hostsForTaskDeletion: read FROM; DB.SearchChatMentionsPage: read FROM; CompanyStore.DeleteCompanyWithAssets: write DELETE; CompanyStore.ListCompanies: read JOIN; malformedIPAssetWarning: read FROM; recomputeAttributionTx: read/write FROM/UPDATE; AssetStore.AttachAssetsToTask: read/write FROM/UPDATE; AssetStore.DetachAssetFromTask: write UPDATE; AssetStore.IntentAssets: read JOIN; AssetStore.RegisterTaskAssetScopes: read FROM; AssetStore.SetTaskAssetSource: read JOIN; AssetStore.HostsByTaskWithSources: read FROM; AssetStore.ListUntestedAssetsWithSources: read FROM; AssetStore.TaskCoverageWithSources: read FROM; AssetStore.BuildCoverageGraph: read FROM; AssetStore.ListUntestedAssets: read FROM; AssetStore.TaskCoverage: read FROM; DB.DeleteTaskCascadePrepared: lock/read/write DELETE/FROM/LOCK/UPDATE; insertTaskCompanies: read/write FROM/UPDATE · db-pg-access-assets-ac89e52754 · db-pg-access-assets-bfe357fb46 · db-pg-access-assets-c2af0e3866 · db-pg-access-assets-08e4edbca3 · db-pg-access-assets-b7f549daa9 · db-pg-access-assets-920527bebb · db-pg-access-assets-34e71511f9 · db-pg-access-assets-3407544ec0 · db-pg-access-assets-f73d325aea · db-pg-access-assets-ec341d7f00 · db-pg-access-assets-474e1ac90a · db-pg-access-assets-689a6eff52 · db-pg-access-assets-3c600226f2 · db-pg-access-assets-b189a1320e · db-pg-access-assets-0e43349555 · db-pg-access-assets-fabc282c04 · db-pg-access-assets-9727e52143 · db-pg-access-assets-381590ebb9 · db-pg-access-assets-2ef5a4abe7 · db-pg-access-assets-292f7c7f0e · db-pg-access-assets-3d08c5ef9a · db-pg-access-assets-49f3abc241 · db-pg-access-assets-d244c644c2 · db-pg-access-assets-1c56c5bc53 · db-pg-access-assets-7bb8deead0 · db-pg-access-assets-f45086ef34 · db-pg-access-assets-5d280f205c · db-pg-access-assets-ac91a2052c · db-pg-access-assets-9923205705 · db-pg-access-assets-67058e4a73 · db-pg-access-assets-08131b651c · db-pg-access-assets-1568fdd7ed · db-pg-access-assets-697813c1b5 · db-pg-access-assets-c773b403af · db-pg-access-assets-9b183fabbc · db-pg-access-assets-71a2b29744 · db-pg-access-assets-52bc975351 · db-pg-access-assets-067d17d02d · db-pg-access-assets-32bf6ec1b3 · db-pg-access-assets-4e75062863 · db-pg-access-assets-e1e6c3d434 · db-pg-access-assets-4cfef63802 · db-pg-access-assets-046fd89c65 · db-pg-access-assets-429d7e60de · db-pg-access-assets-439a90dd4a · db-pg-access-assets-cab0f39b8c
- 계약 사용 · task_asset_links · AssetStore.AttachAssetsToTask: write INSERT; AssetStore.IntentAssets: read JOIN; AssetStore.SetTaskAssetSource: write INSERT; AssetStore.hydrateTaskAssetSources: read FROM; insertTaskCompanies: write INSERT · db-pg-access-task-asset-links-0ea29c2581 · db-pg-access-task-asset-links-13253489dd · db-pg-access-task-asset-links-49ecbd9233 · db-pg-access-task-asset-links-ce059baee2 · db-pg-access-task-asset-links-5a2f3376f0
- 구현 담당 · Tool, MCP, Skill 조립 · 기능 단계의 직접 상태/실행 구현 · feature-impl-7
insert_assets는 model이 task_id를 정하지 못하게 하고 runtime의 ToolSet.taskID를 사용한다. 각 성공 item은 asset을 intent owner에 anchor하고 task_asset_links source/source summary를 갱신한다. 최상위로 명시 삽입된 item만 그 자산 유형의 보수적 task_scope를 자동 추가하며, 내부 파생 asset이 scope를 연쇄 확장하지 않게 한다. Asset tool
처리 순서는 item별 gate → validate/upsert → provenance/anchor → auto scope다. 배열 전체 transaction은 아니므로 앞 item 성공 뒤 뒤 item이 실패할 수 있다. caller는 전체 성공을 가정하지 말고 index별 결과를 확인해야 한다.
Company scope와 task scope
Company scope와 task scope · 관계
이 대상이 사용하는 구현·계약·의존 · 4개
- 계약 사용 · company_scope · CompanyStore.GetScope: read FROM; CompanyStore.UpdateScopeInputsChecked: write DELETE; insertScopeRuleTx: write INSERT; recomputeAttributionTx: read JOIN; resolveCompanyWithICP: read FROM · db-pg-access-company-scope-7e40c33b76 · db-pg-access-company-scope-5d479bbe19 · db-pg-access-company-scope-cc48b3bcc4 · db-pg-access-company-scope-2dcedff17b · db-pg-access-company-scope-910c65209a
- 계약 사용 · task_scope · AssetStore.GetByIDsInScope: read JOIN; AssetStore.QueryDSLInScope: read JOIN; AssetStore.HostsByTaskWithSources: read JOIN; AssetStore.ListTaskScopeWithSources: read FROM; AssetStore.ListUntestedAssetsWithSources: read JOIN; AssetStore.TaskCoverageWithSources: read FROM/JOIN; DB.TaskCompanyIDs: read FROM; DB.hydrateTasksContext: read FROM; AssetStore.BuildCoverageGraph: read JOIN; AssetStore.DeleteTaskScope: write DELETE; AssetStore.ListTaskScope: read FROM; AssetStore.ListUntestedAssets: read JOIN; AssetStore.TaskCoverage: read FROM/JOIN; AssetStore.upsertTaskScopeResult: write INSERT; insertTaskCompanies: write INSERT · db-pg-access-task-scope-d270c90e96 · db-pg-access-task-scope-2050f94c23 · db-pg-access-task-scope-e03dcb28ba · db-pg-access-task-scope-205ddab78f · db-pg-access-task-scope-00288db9f0 · db-pg-access-task-scope-bb64288ddc · db-pg-access-task-scope-f3d7ab74f1 · db-pg-access-task-scope-4663d5f3d5 · db-pg-access-task-scope-3038821322 · db-pg-access-task-scope-d4523a62f2 · db-pg-access-task-scope-a749a30b74 · db-pg-access-task-scope-d824d02d68 · db-pg-access-task-scope-c99ebf71ad · db-pg-access-task-scope-6fe50806ec · db-pg-access-task-scope-8ffdf9a4ad
- 구현 담당 · PostgreSQL 책임 영역 · 기능 단계의 직접 상태/실행 구현 · feature-impl-8
- 계약 사용 · Task scope와 실제 outbound의 차이 · 기능/모듈이 적용하는 보안·신뢰 규칙 · rule-edge-2
| scope | 지원 kind | 역할 | 강제 범위 |
|---|---|---|---|
company_scope |
domain, ip, cidr, icp, keyword | asset의 조직 귀속과 agent 검색 문맥 | task outbound authorization 자체가 아님 |
task_scope |
company, root_domain, subdomain, ip, cidr, icp, keyword | task의 범위 edge와 coverage denominator | 일부 asset query/tool gate의 입력; 모든 egress 중앙 강제는 아님 |
| task intercept rule | exact/fuzzy domain·IP·URL, CIDR + block/allow | intent anchor/agent insert의 사전 gate | 해당 호출 지점에 한정 |
| global asset intercept | 같은 match kind의 block | 모든 task의 금지 자산 | 해당 호출 지점에 한정 |
CIDR parser는 IPv4 /16보다 넓고 IPv6 /32보다 넓은 입력을 거부하며, bare public suffix도 scope로 허용하지 않는다. bare IP는 host CIDR(/32, /128)로 정규화한다. Task scope upsert는 unique 규칙으로 idempotent하다. Task scope Company scope
Block/allow 판정 순서와 장애 정책
Block/allow 판정 순서와 장애 정책 · 관계
이 대상이 사용하는 구현·계약·의존 · 1개
- 계약 사용 · Asset block/allow gate · 기능/모듈이 적용하는 보안·신뢰 규칙 · rule-edge-1
- enabled global block과 task block 중 하나라도 domain/IP/URL/CIDR 후보에 맞으면 거부한다.
- block에 맞지 않고 enabled task allow rule이 하나 이상 있으면, allow 중 어느 것도 맞지 않는 자산을 거부한다.
- enabled allow rule이 없으면 whitelist 단계는 비활성이다.
같은 evaluator를 쓰더라도 caller의 조회 실패 정책이 다르다.
| 호출 지점 | rule 조회 실패 | 결과 |
|---|---|---|
Planner/Main add_intent anchor |
오류를 반환 | intent 생성 중단, fail-closed에 가까움 |
Agent insert_assets |
오류를 무시하고 읽힌 rule 집합으로 진행 | asset insert가 진행될 수 있음 |
| 이미 저장된 asset check | 오류 반환 | caller가 중단 여부 결정 |
따라서 “규칙이 존재한다”만으로 모든 네트워크 동작이 막힌다고 결론내릴 수 없다. 정확한 우회면은 scope와 egress gap을 본다. Gate 구현
Coverage 분모와 tested 신호
Coverage 분모와 tested 신호 · 관계
이 대상이 사용하는 구현·계약·의존 · 2개
- 계약 사용 · exploration_anchors · hostsForTaskDeletion: read FROM; ExplorationStore.NodeAssets: read FROM; ExplorationStore.AddNode: write INSERT; ExplorationStore.Anchor: write INSERT; ExplorationStore.AssetRefs: read FROM; AssetStore.IntentAssets: read JOIN; AssetStore.HostsByTaskWithSources: read FROM; AssetStore.ListUntestedAssetsWithSources: read FROM/JOIN; AssetStore.TaskCoverageWithSources: read FROM/JOIN; AssetStore.BuildCoverageGraph: read FROM/JOIN; AssetStore.ListUntestedAssets: read FROM; AssetStore.TaskCoverage: read FROM; DB.DeleteTaskCascadePrepared: lock/read FROM/LOCK · db-pg-access-exploration-anchors-5a27ac6eca · db-pg-access-exploration-anchors-cddd26dae2 · db-pg-access-exploration-anchors-834586a2d0 · db-pg-access-exploration-anchors-f5ce264565 · db-pg-access-exploration-anchors-a45cbd8b32 · db-pg-access-exploration-anchors-8a1db68a70 · db-pg-access-exploration-anchors-6bdbe05e5c · db-pg-access-exploration-anchors-94dc4ab10f · db-pg-access-exploration-anchors-ef2bc724eb · db-pg-access-exploration-anchors-a6e404bec9 · db-pg-access-exploration-anchors-386313636a · db-pg-access-exploration-anchors-8e3b76a0f7 · db-pg-access-exploration-anchors-fc3554358c
- 구현 담당 · PostgreSQL 책임 영역 · 기능 단계의 직접 상태/실행 구현 · feature-impl-9
coverage가 켜진 task는 task_scope, task asset link, intent anchor와 graph 상태를 이용해 전체/테스트됨/미테스트 자산을 계산하고 graph API에 노출한다. coverage_enabled=false는 coverage tool/일부 분모 누적을 제한하지만 모든 asset 저장을 끄는 설정은 아니다. 특히 agent의 최상위 insert_assets는 주석상 task scope를 coverage toggle과 독립적으로 누적하는 반면 worker 시작 시 anchor 자산 auto scope는 toggle을 확인한다. 두 경로를 같은 규칙으로 가정하면 안 된다. Task assets Worker
“tested”는 intent/anchor/상태를 바탕으로 한 시스템 coverage 표지다. 특정 취약점 class의 충분한 방법론, 인증 역할 조합, 시간 변화까지 소진했다는 증거는 아니다.
조립됐지만 실행 진입점이 없는 비 AI enrichment
조립됐지만 실행 진입점이 없는 비 AI enrichment · 관계
이 대상이 사용하는 구현·계약·의존 · 1개
- 구현 담당 · Tool, MCP, Skill 조립 · 기능 단계의 직접 상태/실행 구현 · feature-impl-10
Manager는 enrichment engine을 만들고 worker의 ToolSet.SetEnrich까지 호출한다. 그러나 이 snapshot의 production 코드에서 ResolveDomain과 ProbeSite를 호출하는 곳은 interface 선언과 method 정의 밖에 없다. 따라서 자산이 들어온 뒤 DNS/HTTP 보강이 자동 실행된다고 볼 수 없다. 현재 상태는 실행 능력이 조립됐지만 trigger가 연결되지 않은 dormant 경로다. Enrichment Tool 조립
호출자가 연결될 경우 engine 자체는 bounded queue와 worker pool을 갖고 (kind, asset id)를 5분 cooldown으로 dedup한다. queue가 가득 차면 job을 로그 후 버린다. DNS는 dnsx로 A/AAAA/CNAME을 조회해 IP/subdomain을 upsert한다. HTTP probe는 최대 12초, redirect를 따라가지 않고, body를 1 MiB까지 읽어 status/length/title을 갱신한다. capture proxy 주소는 요청마다 해석되며 비활성일 때 direct로 갈 수 있다. 이는 구현된 capability 설명이며 현재 실행 흐름 설명은 아니다.
코드 주석은 DNS를 ungated라 명시한다. HTTP probe에 전달되기 전 caller가 어떤 gate를 적용했는지와 direct fallback을 함께 검토해야 한다. queue/cooldown은 영속 상태가 아니므로 process restart 후 다시 실행될 수 있다.
오해하기 쉬운 경계
- asset의
company_id는 ownership 해석이지 bug-bounty authorization의 독립 증거가 아니다. task_scope가 있다고 모든 Bash/WebFetch/MCP/custom tool destination이 그 범위로 제한되는 것은 아니다.- endpoint URL unique는 method까지 포함하지만 request body·role·tenant 조합 coverage를 표현하지 않는다.
- enrichment는 현재 자동 호출되지 않는다. 장래 trigger가 연결돼도 best-effort queue이며 실패/drop이 coverage gap으로 자동 승격되는 코드는 없다.
- ScopeSentry import 성공은 upstream 데이터의 최신성·정확성·허가를 검증하지 않는다.
- task inheritance/source 관계로 보이는 asset과 직접 task asset을 UI/API가 구별하는 경로가 있으므로 provenance 없이 flat list로 합치지 않는다.