167 lines
5.1 KiB
TypeScript
167 lines
5.1 KiB
TypeScript
// For legal and security concerns, we typically only allow Web Fetch to access
|
|
// domains that the user has provided in some form. However, we make an
|
|
// exception for a list of preapproved domains that are code-related.
|
|
//
|
|
// SECURITY WARNING: These preapproved domains are ONLY for WebFetch (GET requests only).
|
|
// The sandbox system deliberately does NOT inherit this list for network restrictions,
|
|
// as arbitrary network access (POST, uploads, etc.) to these domains could enable
|
|
// data exfiltration. Some domains like huggingface.co, kaggle.com, and nuget.org
|
|
// allow file uploads and would be dangerous for unrestricted network access.
|
|
//
|
|
// See test/utils/sandbox/webfetch-preapproved-separation.test.ts for verification
|
|
// that sandbox network restrictions require explicit user permission rules.
|
|
|
|
export const PREAPPROVED_HOSTS = new Set([
|
|
// Anthropic
|
|
'platform.claude.com',
|
|
'code.claude.com',
|
|
'modelcontextprotocol.io',
|
|
'github.com/anthropics',
|
|
'agentskills.io',
|
|
|
|
// Top Programming Languages
|
|
'docs.python.org', // Python
|
|
'en.cppreference.com', // C/C++ reference
|
|
'docs.oracle.com', // Java
|
|
'learn.microsoft.com', // C#/.NET
|
|
'developer.mozilla.org', // JavaScript/Web APIs (MDN)
|
|
'go.dev', // Go
|
|
'pkg.go.dev', // Go docs
|
|
'www.php.net', // PHP
|
|
'docs.swift.org', // Swift
|
|
'kotlinlang.org', // Kotlin
|
|
'ruby-doc.org', // Ruby
|
|
'doc.rust-lang.org', // Rust
|
|
'www.typescriptlang.org', // TypeScript
|
|
|
|
// Web & JavaScript Frameworks/Libraries
|
|
'react.dev', // React
|
|
'angular.io', // Angular
|
|
'vuejs.org', // Vue.js
|
|
'nextjs.org', // Next.js
|
|
'expressjs.com', // Express.js
|
|
'nodejs.org', // Node.js
|
|
'bun.sh', // Bun
|
|
'jquery.com', // jQuery
|
|
'getbootstrap.com', // Bootstrap
|
|
'tailwindcss.com', // Tailwind CSS
|
|
'd3js.org', // D3.js
|
|
'threejs.org', // Three.js
|
|
'redux.js.org', // Redux
|
|
'webpack.js.org', // Webpack
|
|
'jestjs.io', // Jest
|
|
'reactrouter.com', // React Router
|
|
|
|
// Python Frameworks & Libraries
|
|
'docs.djangoproject.com', // Django
|
|
'flask.palletsprojects.com', // Flask
|
|
'fastapi.tiangolo.com', // FastAPI
|
|
'pandas.pydata.org', // Pandas
|
|
'numpy.org', // NumPy
|
|
'www.tensorflow.org', // TensorFlow
|
|
'pytorch.org', // PyTorch
|
|
'scikit-learn.org', // Scikit-learn
|
|
'matplotlib.org', // Matplotlib
|
|
'requests.readthedocs.io', // Requests
|
|
'jupyter.org', // Jupyter
|
|
|
|
// PHP Frameworks
|
|
'laravel.com', // Laravel
|
|
'symfony.com', // Symfony
|
|
'wordpress.org', // WordPress
|
|
|
|
// Java Frameworks & Libraries
|
|
'docs.spring.io', // Spring
|
|
'hibernate.org', // Hibernate
|
|
'tomcat.apache.org', // Tomcat
|
|
'gradle.org', // Gradle
|
|
'maven.apache.org', // Maven
|
|
|
|
// .NET & C# Frameworks
|
|
'asp.net', // ASP.NET
|
|
'dotnet.microsoft.com', // .NET
|
|
'nuget.org', // NuGet
|
|
'blazor.net', // Blazor
|
|
|
|
// Mobile Development
|
|
'reactnative.dev', // React Native
|
|
'docs.flutter.dev', // Flutter
|
|
'developer.apple.com', // iOS/macOS
|
|
'developer.android.com', // Android
|
|
|
|
// Data Science & Machine Learning
|
|
'keras.io', // Keras
|
|
'spark.apache.org', // Apache Spark
|
|
'huggingface.co', // Hugging Face
|
|
'www.kaggle.com', // Kaggle
|
|
|
|
// Databases
|
|
'www.mongodb.com', // MongoDB
|
|
'redis.io', // Redis
|
|
'www.postgresql.org', // PostgreSQL
|
|
'dev.mysql.com', // MySQL
|
|
'www.sqlite.org', // SQLite
|
|
'graphql.org', // GraphQL
|
|
'prisma.io', // Prisma
|
|
|
|
// Cloud & DevOps
|
|
'docs.aws.amazon.com', // AWS
|
|
'cloud.google.com', // Google Cloud
|
|
'learn.microsoft.com', // Azure
|
|
'kubernetes.io', // Kubernetes
|
|
'www.docker.com', // Docker
|
|
'www.terraform.io', // Terraform
|
|
'www.ansible.com', // Ansible
|
|
'vercel.com/docs', // Vercel
|
|
'docs.netlify.com', // Netlify
|
|
'devcenter.heroku.com', // Heroku
|
|
|
|
// Testing & Monitoring
|
|
'cypress.io', // Cypress
|
|
'selenium.dev', // Selenium
|
|
|
|
// Game Development
|
|
'docs.unity.com', // Unity
|
|
'docs.unrealengine.com', // Unreal Engine
|
|
|
|
// Other Essential Tools
|
|
'git-scm.com', // Git
|
|
'nginx.org', // Nginx
|
|
'httpd.apache.org', // Apache HTTP Server
|
|
])
|
|
|
|
// Split once at module load so lookups are O(1) Set.has() for the common
|
|
// hostname-only case, falling back to a small per-host path-prefix list
|
|
// for the handful of path-scoped entries (e.g., "github.com/anthropics").
|
|
const { HOSTNAME_ONLY, PATH_PREFIXES } = (() => {
|
|
const hosts = new Set<string>()
|
|
const paths = new Map<string, string[]>()
|
|
for (const entry of PREAPPROVED_HOSTS) {
|
|
const slash = entry.indexOf('/')
|
|
if (slash === -1) {
|
|
hosts.add(entry)
|
|
} else {
|
|
const host = entry.slice(0, slash)
|
|
const path = entry.slice(slash)
|
|
const prefixes = paths.get(host)
|
|
if (prefixes) prefixes.push(path)
|
|
else paths.set(host, [path])
|
|
}
|
|
}
|
|
return { HOSTNAME_ONLY: hosts, PATH_PREFIXES: paths }
|
|
})()
|
|
|
|
export function isPreapprovedHost(hostname: string, pathname: string): boolean {
|
|
if (HOSTNAME_ONLY.has(hostname)) return true
|
|
const prefixes = PATH_PREFIXES.get(hostname)
|
|
if (prefixes) {
|
|
for (const p of prefixes) {
|
|
// Enforce path segment boundaries: "/anthropics" must not match
|
|
// "/anthropics-evil/malware". Only exact match or a "/" after the
|
|
// prefix is allowed.
|
|
if (pathname === p || pathname.startsWith(p + '/')) return true
|
|
}
|
|
}
|
|
return false
|
|
}
|