Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: more robust calculation of default location for code generation #1095

Merged
merged 1 commit into from
Mar 7, 2024
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zenstack-monorepo",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"description": "",
"scripts": {
"build": "pnpm -r build",
Expand Down
2 changes: 1 addition & 1 deletion packages/ide/jetbrains/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

group = "dev.zenstack"
version = "2.0.0-alpha.4"
version = "2.0.0-alpha.5"

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion packages/ide/jetbrains/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jetbrains",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"displayName": "ZenStack JetBrains IDE Plugin",
"description": "ZenStack JetBrains IDE plugin",
"homepage": "https://zenstack.dev",
Expand Down
2 changes: 1 addition & 1 deletion packages/language/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/language",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"displayName": "ZenStack modeling language compiler",
"description": "ZenStack modeling language compiler",
"homepage": "https://zenstack.dev",
Expand Down
2 changes: 1 addition & 1 deletion packages/misc/redwood/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/redwood",
"displayName": "ZenStack RedwoodJS Integration",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"description": "CLI and runtime for integrating ZenStack with RedwoodJS projects.",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/openapi/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/openapi",
"displayName": "ZenStack Plugin and Runtime for OpenAPI",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"description": "ZenStack plugin and runtime supporting OpenAPI",
"main": "index.js",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/swr/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/swr",
"displayName": "ZenStack plugin for generating SWR hooks",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"description": "ZenStack plugin for generating SWR hooks",
"main": "index.js",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/tanstack-query/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/tanstack-query",
"displayName": "ZenStack plugin for generating tanstack-query hooks",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"description": "ZenStack plugin for generating tanstack-query hooks",
"main": "index.js",
"exports": {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/trpc/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/trpc",
"displayName": "ZenStack plugin for tRPC",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"description": "ZenStack plugin for tRPC",
"main": "index.js",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/runtime",
"displayName": "ZenStack Runtime Library",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"description": "Runtime of ZenStack for both client-side and server-side environments.",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"publisher": "zenstack",
"displayName": "ZenStack Language Tools",
"description": "Build scalable web apps with minimum code by defining authorization and validation rules inside the data schema that closer to the database",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"author": {
"name": "ZenStack Team"
},
Expand Down
32 changes: 21 additions & 11 deletions packages/schema/src/plugins/plugin-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PluginGlobalOptions } from '@zenstackhq/sdk';
import fs from 'fs';
import path from 'path';
import { PluginRunnerOptions } from '../cli/plugin-runner';
import { getPackageManager } from '../utils/pkg-utils';

export const ALL_OPERATION_KINDS: PolicyOperationKind[] = ['create', 'update', 'postUpdate', 'read', 'delete'];

Comment on lines 3 to 9
Copy link
Contributor

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [17-17]

Detected possible user input going into path.join or path.resolve functions in several places within getNodeModulesFolder, ensureDefaultOutputFolder, and getDefaultOutputFolder functions. This could potentially lead to a path traversal vulnerability. Ensure that any user input or variable paths are sanitized or validated before use to prevent unauthorized file system access.

Also applies to: 18-18, 20-20, 31-31, 64-64, 77-77

Expand Down Expand Up @@ -76,23 +77,32 @@ export function getDefaultOutputFolder(globalOptions?: PluginGlobalOptions) {
return path.resolve(globalOptions.output);
}

// Find the real runtime module path, it might be a symlink in pnpm
let runtimeModulePath = require.resolve('@zenstackhq/runtime');

// for testing, use the local node_modules
if (process.env.ZENSTACK_TEST === '1') {
// handle the case when running as tests, resolve relative to CWD
runtimeModulePath = path.resolve(path.join(process.cwd(), 'node_modules', '@zenstackhq', 'runtime'));
return path.join(process.cwd(), 'node_modules', DEFAULT_RUNTIME_LOAD_PATH);
}

if (runtimeModulePath) {
// start with the parent folder of @zenstackhq, supposed to be a node_modules folder
while (!runtimeModulePath.endsWith('@zenstackhq') && runtimeModulePath !== '/') {
const { projectRoot } = getPackageManager(__dirname);
if (fs.existsSync(path.join(projectRoot, 'node_modules'))) {
// use the located node_modules folder
return path.join(projectRoot, 'node_modules', DEFAULT_RUNTIME_LOAD_PATH);
} else {
// unable to locate a node_modules folder, fallback to where the runtime
// package resides

// find the real runtime module path, it might be a symlink in pnpm
let runtimeModulePath = require.resolve('@zenstackhq/runtime');

if (runtimeModulePath) {
// start with the parent folder of @zenstackhq, supposed to be a node_modules folder
while (!runtimeModulePath.endsWith('@zenstackhq') && runtimeModulePath !== '/') {
runtimeModulePath = path.join(runtimeModulePath, '..');
}
runtimeModulePath = path.join(runtimeModulePath, '..');
}
runtimeModulePath = path.join(runtimeModulePath, '..');
const modulesFolder = getNodeModulesFolder(runtimeModulePath);
return modulesFolder ? path.join(modulesFolder, DEFAULT_RUNTIME_LOAD_PATH) : undefined;
}
const modulesFolder = getNodeModulesFolder(runtimeModulePath);
return modulesFolder ? path.join(modulesFolder, DEFAULT_RUNTIME_LOAD_PATH) : undefined;
}

/**
Expand Down
43 changes: 24 additions & 19 deletions packages/schema/src/utils/pkg-utils.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import fs from 'node:fs';
import path from 'node:path';
import { execSync } from './exec-utils';
import { match } from 'ts-pattern';

export type PackageManagers = 'npm' | 'yarn' | 'pnpm';

/**
* A type named FindUp that takes a type parameter e which extends boolean.
* If e extends true, it returns a union type of string[] or undefined.
* A type named FindUp that takes a type parameter e which extends boolean.
* If e extends true, it returns a union type of string[] or undefined.
* If e does not extend true, it returns a union type of string or undefined.
*
* @export
* @template e A type parameter that extends boolean
*/
export type FindUp<e extends boolean> = e extends true ? string[] | undefined : string | undefined
export type FindUp<e extends boolean> = e extends true ? string[] | undefined : string | undefined;
/**
* Find and return file paths by searching parent directories based on the given names list and current working directory (cwd) path.
* Optionally return a single path or multiple paths.
* If multiple allowed, return all paths found.
* Find and return file paths by searching parent directories based on the given names list and current working directory (cwd) path.
* Optionally return a single path or multiple paths.
* If multiple allowed, return all paths found.
* If no paths are found, return undefined.
*
* @export
Expand All @@ -27,7 +28,12 @@ export type FindUp<e extends boolean> = e extends true ? string[] | undefined :
* @param [result=[]] An array of strings representing the accumulated results used in multiple results
* @returns Path(s) to a specific file or folder within the directory or parent directories
*/
export function findUp<e extends boolean = false>(names: string[], cwd: string = process.cwd(), multiple: e = false as e, result: string[] = []): FindUp<e> {
export function findUp<e extends boolean = false>(
names: string[],
cwd: string = process.cwd(),
multiple: e = false as e,
result: string[] = []
): FindUp<e> {
if (!names.some((name) => !!name)) return undefined;
const target = names.find((name) => fs.existsSync(path.join(cwd, name)));
if (multiple == false && target) return path.join(cwd, target) as FindUp<e>;
Expand All @@ -37,23 +43,22 @@ export function findUp<e extends boolean = false>(names: string[], cwd: string =
return findUp(names, up, multiple, result);
}

function getPackageManager(projectPath = '.'): PackageManagers {
const lockFile = findUp(['yarn.lock', 'pnpm-lock.yaml', 'package-lock.json'], projectPath);
export function getPackageManager(searchStartPath = '.') {
const lockFile = findUp(['yarn.lock', 'pnpm-lock.yaml', 'package-lock.json'], searchStartPath);

if (!lockFile) {
// default use npm
return 'npm';
return { packageManager: 'npm', lockFile: undefined, projectRoot: searchStartPath };
}

switch (path.basename(lockFile)) {
case 'yarn.lock':
return 'yarn';
case 'pnpm-lock.yaml':
return 'pnpm';
default:
return 'npm';
}
const packageManager = match(path.basename(lockFile))
.with('yarn.lock', () => 'yarn')
.with('pnpm-lock.yaml', () => 'pnpm')
.otherwise(() => 'npm');

return { packageManager, lockFile, projectRoot: path.dirname(lockFile) };
}

export function installPackage(
pkg: string,
dev: boolean,
Expand Down Expand Up @@ -106,7 +111,7 @@ export function ensurePackage(
}

/**
* A function that searches for the nearest package.json file starting from the provided search path or the current working directory if no search path is provided.
* A function that searches for the nearest package.json file starting from the provided search path or the current working directory if no search path is provided.
* It iterates through the directory structure going one level up at a time until it finds a package.json file. If no package.json file is found, it returns undefined.
* @deprecated Use findUp instead @see findUp
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/sdk",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"description": "ZenStack plugin development SDK",
"main": "index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/server",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"displayName": "ZenStack Server-side Adapters",
"description": "ZenStack server-side adapters",
"homepage": "https://zenstack.dev",
Expand Down
9 changes: 5 additions & 4 deletions packages/server/src/shared.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import type { ModelMeta, PolicyDef, ZodSchemas } from '@zenstackhq/runtime';
import { DEFAULT_RUNTIME_LOAD_PATH, type ModelMeta, type PolicyDef, type ZodSchemas } from '@zenstackhq/runtime';
import path from 'path';
import { AdapterBaseOptions } from './types';

Comment on lines 1 to 5
Copy link
Contributor

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [33-33]

Detected possible user input going into path.join or path.resolve functions in the getDefaultModelMeta, getDefaultPolicy, and getDefaultZodSchemas functions. This could potentially lead to a path traversal vulnerability. Ensure that the loadPath parameter is sanitized or validated before use to prevent unauthorized file system access.

Also applies to: 61-61, 92-92

Expand Down Expand Up @@ -39,7 +39,8 @@ export function getDefaultModelMeta(loadPath: string | undefined): ModelMeta {
if (process.env.ZENSTACK_TEST === '1' && !loadPath) {
try {
// special handling for running as tests, try resolving relative to CWD
return require(path.join(process.cwd(), 'node_modules', '.zenstack', 'model-meta')).default;
return require(path.join(process.cwd(), 'node_modules', DEFAULT_RUNTIME_LOAD_PATH, 'model-meta'))
.default;
} catch {
throw new Error('Model meta cannot be loaded. Please make sure "zenstack generate" has been run.');
}
Expand All @@ -66,7 +67,7 @@ export function getDefaultPolicy(loadPath: string | undefined): PolicyDef {
if (process.env.ZENSTACK_TEST === '1' && !loadPath) {
try {
// special handling for running as tests, try resolving relative to CWD
return require(path.join(process.cwd(), 'node_modules', '.zenstack', 'policy')).default;
return require(path.join(process.cwd(), 'node_modules', DEFAULT_RUNTIME_LOAD_PATH, 'policy')).default;
} catch {
throw new Error(
'Policy definition cannot be loaded from default location. Please make sure "zenstack generate" has been run.'
Expand Down Expand Up @@ -97,7 +98,7 @@ export function getDefaultZodSchemas(loadPath: string | undefined): ZodSchemas |
if (process.env.ZENSTACK_TEST === '1' && !loadPath) {
try {
// special handling for running as tests, try resolving relative to CWD
return require(path.join(process.cwd(), 'node_modules', '.zenstack', 'zod'));
return require(path.join(process.cwd(), 'node_modules', DEFAULT_RUNTIME_LOAD_PATH, 'zod'));
} catch {
return undefined;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/testtools/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/testtools",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.5",
"description": "ZenStack Test Tools",
"main": "index.js",
"private": true,
Expand Down
10 changes: 8 additions & 2 deletions packages/testtools/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { DMMF } from '@prisma/generator-helper';
import type { Model } from '@zenstackhq/language/ast';
import type { AuthUser, CrudContract, EnhancementKind, EnhancementOptions } from '@zenstackhq/runtime';
import {
DEFAULT_RUNTIME_LOAD_PATH,
type AuthUser,
type CrudContract,
type EnhancementKind,
type EnhancementOptions,
} from '@zenstackhq/runtime';
import { getDMMF } from '@zenstackhq/sdk';
import { execSync } from 'child_process';
import * as fs from 'fs';
Comment on lines 2 to 14
Copy link
Contributor

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [46-46]

Detected a call to child_process from a function argument cmd in the run function. This could lead to a command injection if the input is user-controllable. Ensure that user input is correctly sanitized or sandboxed to prevent security vulnerabilities.


📝 NOTE
This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [70-70]

Detected possible user input going into path.join or path.resolve functions in getWorkspaceRoot and getWorkspaceNpmCacheFolder. This could potentially lead to a path traversal vulnerability. Ensure that any user input or variable paths are sanitized or validated before use to prevent unauthorized file system access.

Also applies to: 81-81

Expand Down Expand Up @@ -284,7 +290,7 @@ export async function loadSchema(schema: string, options?: SchemaLoadOptions) {
? path.isAbsolute(opt.output)
? opt.output
: path.join(projectRoot, opt.output)
: path.join(projectRoot, 'node_modules', '.zenstack');
: path.join(projectRoot, 'node_modules', DEFAULT_RUNTIME_LOAD_PATH);

const policy = require(path.join(outputPath, 'policy')).default;
const modelMeta = require(path.join(outputPath, 'model-meta')).default;
Expand Down
Loading