Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/proxy",
"version": "0.2.5",
"version": "0.2.6",
"description": "A CLI tool to run an Express server that proxies CRUD requests to a ZenStack backend",
"main": "index.js",
"publishConfig": {
Expand All @@ -24,7 +24,7 @@
"author": "",
"license": "MIT",
"dependencies": {
"@prisma/adapter-better-sqlite3": "^6.2.1",
"@prisma/adapter-better-sqlite3": "^7.3.0",
"@prisma/adapter-mariadb": "^7.1.0",
"@prisma/adapter-pg": "^6.18.0",
"@zenstackhq/server": "^2.0.0",
Expand Down
33 changes: 23 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 19 additions & 3 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as path from 'path'
import express from 'express'
import cors from 'cors'
import { ZenStackMiddleware } from '@zenstackhq/server/express'
import { GeneratorConfig, ZModelConfig } from './zmodel-parser'
import { ZModelConfig } from './zmodel-parser'
import { getNodeModulesFolder, getPrismaVersion, getZenStackVersion } from './utils/version-utils'
import { blue, grey } from 'colors'
import semver from 'semver'
Expand Down Expand Up @@ -46,6 +46,22 @@ function resolveSQLitePath(filePath: string, prismaSchemaDir: string): string {
return path.join(prismaSchemaDir, filePath)
}

function redactDatabaseUrl(url: string): string {
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The redactDatabaseUrl function is missing a JSDoc comment. All other helper functions in this file (e.g., resolvePrismaSchemaDir on line 19, resolveSQLitePath on line 37, createAdapter on line 65) have JSDoc documentation. Please add a JSDoc comment describing the function's purpose, parameter, and return value to maintain consistency with the established pattern in this file.

Copilot uses AI. Check for mistakes.
try {
const parsedUrl = new URL(url)
if (parsedUrl.password) {
parsedUrl.password = '***'
}
if (parsedUrl.username) {
parsedUrl.username = '***'
}
return parsedUrl.toString()
} catch {
// If URL parsing fails, return the original (might be a file path for SQLite)
return url
}
}
Comment on lines +49 to +63
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid leaking credentials on parse failure (and in query params).

If new URL(url) throws, the current fallback returns the raw string, which can leak secrets. Also, credentials in query params (e.g., ?password=) are not masked. Consider a conservative fallback redaction.

🔒 Suggested fix
 function redactDatabaseUrl(url: string): string {
   try {
     const parsedUrl = new URL(url)
     if (parsedUrl.password) {
       parsedUrl.password = '***'
     }
     if (parsedUrl.username) {
       parsedUrl.username = '***'
     }
+    for (const key of ['password', 'passwd', 'pwd', 'user', 'username']) {
+      if (parsedUrl.searchParams.has(key)) {
+        parsedUrl.searchParams.set(key, '***')
+      }
+    }
     return parsedUrl.toString()
   } catch {
-    // If URL parsing fails, return the original (might be a file path for SQLite)
-    return url
+    // If URL parsing fails, redact userinfo/query credentials conservatively
+    return url
+      .replace(/\/\/([^:@\/?#]+):([^@\/?#]*)@/g, '//***:***@')
+      .replace(/\/\/([^:@\/?#]+)@/g, '//***@')
+      .replace(/([?&](?:password|passwd|pwd|user|username)=)[^&]*/gi, '$1***')
   }
 }
🤖 Prompt for AI Agents
In `@src/server.ts` around lines 49 - 63, The redactDatabaseUrl function currently
returns the raw input on URL parse failure and only masks
parsedUrl.username/password, which can leak credentials and query-params; update
redactDatabaseUrl to (1) when parsing succeeds, also scrub sensitive query
parameters (case-insensitive keys like password, pass, user, username, token,
secret) by replacing their values with '***' in parsedUrl.searchParams before
returning parsedUrl.toString(), and (2) on parse failure, perform a conservative
string-based redaction: mask basic-auth patterns (user:pass@) and mask sensitive
query-like key=value pairs anywhere in the string using a regex that matches the
same keys (replace values with '***') instead of returning the original url.
Ensure you reference and update the existing function redactDatabaseUrl and the
parsedUrl variable handling.


/**
* Create database adapter based on provider
*/
Expand Down Expand Up @@ -79,7 +95,7 @@ function createAdapter(config: ZModelConfig, zmodelSchemaDir: string): any {
case 'postgresql': {
try {
const { PrismaPg } = require('@prisma/adapter-pg')
console.log(grey(`Connecting to PostgreSQL database at: ${url}`))
console.log(grey(`Connecting to PostgreSQL database at: ${redactDatabaseUrl(url)}`))
return new PrismaPg({ connectionString: url })
} catch (error) {
throw new CliError(
Expand All @@ -90,7 +106,7 @@ function createAdapter(config: ZModelConfig, zmodelSchemaDir: string): any {
case 'mysql': {
try {
const { PrismaMariaDB } = require('@prisma/adapter-mariadb')
console.log(grey(`Connecting to MySQL/MariaDB database at: ${url}`))
console.log(grey(`Connecting to MySQL/MariaDB database at: ${redactDatabaseUrl(url)}`))
return new PrismaMariaDB({
url,
})
Expand Down
Loading