{"id":51123602,"url":"https://github.com/devslab-kr/ssrf-guard-js","last_synced_at":"2026-06-25T05:30:33.451Z","repository":{"id":365514824,"uuid":"1272444344","full_name":"devslab-kr/ssrf-guard-js","owner":"devslab-kr","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-17T16:41:30.000Z","size":55,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-17T18:15:19.825Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devslab-kr.png","metadata":{"files":{"readme":"README.ko.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://devslab.kr"]}},"created_at":"2026-06-17T16:03:46.000Z","updated_at":"2026-06-17T16:36:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/devslab-kr/ssrf-guard-js","commit_stats":null,"previous_names":["devslab-kr/ssrf-guard-js"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/devslab-kr/ssrf-guard-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devslab-kr%2Fssrf-guard-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devslab-kr%2Fssrf-guard-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devslab-kr%2Fssrf-guard-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devslab-kr%2Fssrf-guard-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devslab-kr","download_url":"https://codeload.github.com/devslab-kr/ssrf-guard-js/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devslab-kr%2Fssrf-guard-js/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34761844,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-25T05:30:32.736Z","updated_at":"2026-06-25T05:30:33.445Z","avatar_url":"https://github.com/devslab-kr.png","language":"TypeScript","funding_links":["https://devslab.kr"],"categories":[],"sub_categories":[],"readme":"# ssrf-guard-js\n\n[English](README.md) | [문서 사이트](https://devslab-kr.github.io/ssrf-guard-js/)\n\nJavaScript / TypeScript용 SSRF 방어 라이브러리입니다.\n\nJava/JVM용 [`devslab-kr/ssrf-guard`](https://github.com/devslab-kr/ssrf-guard)의\n핵심 보안 모델을 JS/TS로 옮깁니다.\n\n- URL 검증: scheme, host allowlist, port, userinfo, IP literal 차단\n- private/local network IP 분류\n- LLM tool-call JSON 안에 숨어 있는 URL 검사\n- URL, DNS, redirect를 재검증하는 guarded fetch\n\n## 설치\n\n```bash\npnpm add @devslab/ssrf-guard-js\n```\n\n복붙해서 바로 따라 하는 튜토리얼은 문서 사이트를 보세요.\n\nhttps://devslab-kr.github.io/ssrf-guard-js/\n\n## URL 검증\n\n```ts\nimport { validateUrl } from '@devslab/ssrf-guard-js';\n\nvalidateUrl('https://api.example.com/v1', {\n  exactHosts: ['api.example.com'],\n  allowedSchemes: ['https'],\n  allowedPorts: [-1, 443],\n});\n```\n\n`exactHosts`와 `suffixes`가 비어 있으면 아무 host도 허용하지 않습니다.\n기본 동작은 fail-closed입니다.\n\n## LLM Tool Input Guard\n\n```ts\nimport { guardToolInputJson } from '@devslab/ssrf-guard-js';\n\nconst violation = guardToolInputJson(\n  JSON.stringify({ request: { target: 'http://169.254.169.254/latest/meta-data/' } }),\n  { exactHosts: ['api.example.com'] },\n);\n\nif (violation) {\n  return violation;\n}\n```\n\nJSON 전체를 재귀적으로 검사합니다. URL이 nested object, array, 설명 문장 안에 숨어 있어도\n차단됩니다.\n\n## Guarded Fetch\n\n```ts\nimport { safeFetch } from '@devslab/ssrf-guard-js';\n\nconst response = await safeFetch('https://api.example.com/data', {\n  exactHosts: ['api.example.com'],\n  allowedSchemes: ['https'],\n});\n```\n\n`safeFetch`는 URL을 검증하고, DNS 결과가 private/local IP인지 확인하고,\nredirect hop마다 다시 검증합니다.\n\nNode의 built-in `fetch`는 Java Apache HttpClient처럼 socket-level IP pinning API를\n노출하지 않습니다. 위험도가 높은 임의 URL 크롤링은 strict allowlist나 별도 guarded\negress service를 사용하세요.\n\n## Express\n\n```ts\nimport express from 'express';\nimport { createExpressUrlGuard } from '@devslab/ssrf-guard-js';\n\nconst app = express();\napp.use(express.json());\n\napp.post(\n  '/crawl',\n  createExpressUrlGuard({\n    exactHosts: ['example.com'],\n    suffixes: ['example.com'],\n    allowedSchemes: ['https'],\n  }),\n  async (req, res) =\u003e {\n    res.json({ ok: true });\n  },\n);\n```\n\n기본으로 `req.body`와 `req.query`를 검사합니다. 차단해야 할 URL이 있으면 구조화된\n`400` 응답을 반환합니다.\n\n## Vite\n\nVite dev server의 SSR/proxy endpoint가 URL을 받아 server-side fetch를 하는 경우에 사용합니다.\n\n```ts\n// vite.config.ts\nimport { defineConfig } from 'vite';\nimport { ssrfGuardVitePlugin } from '@devslab/ssrf-guard-js/vite';\n\nexport default defineConfig({\n  plugins: [\n    ssrfGuardVitePlugin({\n      routes: ['/api/crawl'],\n      policy: {\n        suffixes: ['example.com'],\n        allowedSchemes: ['https'],\n      },\n    }),\n  ],\n});\n```\n\n기본으로 `url`, `target`, `uri`, `href` query param을 검사합니다.\n예를 들어 아래 요청은 차단됩니다.\n\n```text\n/api/crawl?url=http://169.254.169.254/latest/meta-data/\n```\n\n## LangChain / Agent Tool\n\n`createGuardedToolHandler`는 LangChain에 직접 의존하지 않고 object-input tool 함수를 감쌉니다.\n\n```ts\nimport { DynamicStructuredTool } from '@langchain/core/tools';\nimport { z } from 'zod';\nimport { createGuardedToolHandler, safeFetch } from '@devslab/ssrf-guard-js';\n\nconst policy = {\n  suffixes: ['example.com'],\n  allowedSchemes: ['https'],\n};\n\nexport const fetchUrlTool = new DynamicStructuredTool({\n  name: 'fetch_url',\n  description: 'Fetch an allowed URL',\n  schema: z.object({ url: z.string().url() }),\n  func: createGuardedToolHandler(policy, async ({ url }) =\u003e {\n    const response = await safeFetch(url, policy);\n    return await response.text();\n  }),\n});\n```\n\n모델이 private IP, metadata URL, 허용되지 않은 host를 넘기면 실제 fetch를 하지 않고\n`ssrf_blocked` JSON 문자열을 tool 결과로 반환합니다.\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevslab-kr%2Fssrf-guard-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevslab-kr%2Fssrf-guard-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevslab-kr%2Fssrf-guard-js/lists"}