@askalf/dario 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/proxy.js +14 -19
  2. package/package.json +1 -1
package/dist/proxy.js CHANGED
@@ -12,7 +12,6 @@ import { getAccessToken, getStatus } from './oauth.js';
12
12
  const ANTHROPIC_API = 'https://api.anthropic.com';
13
13
  const DEFAULT_PORT = 3456;
14
14
  const MAX_BODY_BYTES = 10 * 1024 * 1024; // 10 MB — generous for large prompts, prevents abuse
15
- const ALLOWED_PATH_PREFIX = '/v1/'; // Only proxy Anthropic API paths
16
15
  const LOCALHOST = '127.0.0.1';
17
16
  const CORS_ORIGIN = 'http://localhost';
18
17
  function sanitizeError(err) {
@@ -62,14 +61,20 @@ export async function startProxy(opts = {}) {
62
61
  res.end(JSON.stringify(s));
63
62
  return;
64
63
  }
65
- // Only allow proxying to /v1/* paths — block path traversal and SSRF
66
- const urlPath = req.url?.split('?')[0] ?? '';
67
- if (!urlPath.startsWith(ALLOWED_PATH_PREFIX) || urlPath.includes('..') || urlPath.includes('//')) {
64
+ // Allowlisted API paths — only these are proxied (prevents SSRF)
65
+ const rawPath = req.url?.split('?')[0] ?? '';
66
+ const allowedPaths = {
67
+ '/v1/messages': `${ANTHROPIC_API}/v1/messages`,
68
+ '/v1/models': `${ANTHROPIC_API}/v1/models`,
69
+ '/v1/complete': `${ANTHROPIC_API}/v1/complete`,
70
+ };
71
+ const targetBase = allowedPaths[rawPath];
72
+ if (!targetBase) {
68
73
  res.writeHead(403, { 'Content-Type': 'application/json' });
69
- res.end(JSON.stringify({ error: 'Forbidden', message: `Only ${ALLOWED_PATH_PREFIX}* paths are proxied` }));
74
+ res.end(JSON.stringify({ error: 'Forbidden', message: 'Path not allowed' }));
70
75
  return;
71
76
  }
72
- // Only allow POST (Messages API) and GET (models, etc)
77
+ // Only allow POST (Messages API) and GET (models)
73
78
  if (req.method !== 'POST' && req.method !== 'GET') {
74
79
  res.writeHead(405, { 'Content-Type': 'application/json' });
75
80
  res.end(JSON.stringify({ error: 'Method not allowed' }));
@@ -96,18 +101,8 @@ export async function startProxy(opts = {}) {
96
101
  if (verbose) {
97
102
  console.log(`[dario] #${requestCount} ${req.method} ${req.url}`);
98
103
  }
99
- // Forward to Anthropic construct URL safely to prevent SSRF
100
- const targetUrl = new URL(urlPath, ANTHROPIC_API);
101
- // Preserve query string from original request
102
- const queryString = req.url?.includes('?') ? req.url.split('?')[1] : '';
103
- if (queryString)
104
- targetUrl.search = queryString;
105
- // Verify the constructed URL still points to Anthropic (defense in depth)
106
- if (targetUrl.origin !== ANTHROPIC_API) {
107
- res.writeHead(403, { 'Content-Type': 'application/json' });
108
- res.end(JSON.stringify({ error: 'Forbidden' }));
109
- return;
110
- }
104
+ // Build target URL from allowlist (no user input in URL construction)
105
+ const targetUrl = targetBase;
111
106
  // Merge any client-provided beta flags with the required oauth flag
112
107
  const clientBeta = req.headers['anthropic-beta'];
113
108
  const betaFlags = new Set(['oauth-2025-04-20']);
@@ -125,7 +120,7 @@ export async function startProxy(opts = {}) {
125
120
  'anthropic-beta': [...betaFlags].join(','),
126
121
  'x-app': 'cli',
127
122
  };
128
- const upstream = await fetch(targetUrl.toString(), {
123
+ const upstream = await fetch(targetUrl, {
129
124
  method: req.method ?? 'POST',
130
125
  headers,
131
126
  body: body.length > 0 ? body : undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Use your Claude subscription as an API. Two commands, no API key. OAuth bridge for Claude Max/Pro.",
5
5
  "type": "module",
6
6
  "bin": {