fix: validate path in fs (#3152)
* fix: validate path in fs * fix other fs issue * fix test * fix test * fix test * fix: do not check file exist on model status validation * chore: bump version * remove copyFileSync method --------- Co-authored-by: Louis <louis@jan.ai>
This commit is contained in:
parent
b28a9766b9
commit
dddf3500ec
@ -64,10 +64,6 @@ const appendFileSync = (...args: any[]) => globalThis.core.api?.appendFileSync(.
|
|||||||
const syncFile: (src: string, dest: string) => Promise<any> = (src, dest) =>
|
const syncFile: (src: string, dest: string) => Promise<any> = (src, dest) =>
|
||||||
globalThis.core.api?.syncFile(src, dest)
|
globalThis.core.api?.syncFile(src, dest)
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy file sync.
|
|
||||||
*/
|
|
||||||
const copyFileSync = (...args: any[]) => globalThis.core.api?.copyFileSync(...args)
|
|
||||||
|
|
||||||
const copyFile: (src: string, dest: string) => Promise<void> = (src, dest) =>
|
const copyFile: (src: string, dest: string) => Promise<void> = (src, dest) =>
|
||||||
globalThis.core.api?.copyFile(src, dest)
|
globalThis.core.api?.copyFile(src, dest)
|
||||||
@ -95,7 +91,6 @@ export const fs = {
|
|||||||
rm,
|
rm,
|
||||||
unlinkSync,
|
unlinkSync,
|
||||||
appendFileSync,
|
appendFileSync,
|
||||||
copyFileSync,
|
|
||||||
copyFile,
|
copyFile,
|
||||||
syncFile,
|
syncFile,
|
||||||
fileStat,
|
fileStat,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { resolve, sep } from 'path'
|
import { resolve, sep } from 'path'
|
||||||
import { DownloadEvent } from '../../../types/api'
|
import { DownloadEvent } from '../../../types/api'
|
||||||
import { normalizeFilePath } from '../../helper/path'
|
import { normalizeFilePath, validatePath } from '../../helper/path'
|
||||||
import { getJanDataFolderPath } from '../../helper'
|
import { getJanDataFolderPath } from '../../helper'
|
||||||
import { DownloadManager } from '../../helper/download'
|
import { DownloadManager } from '../../helper/download'
|
||||||
import { createWriteStream, renameSync } from 'fs'
|
import { createWriteStream, renameSync } from 'fs'
|
||||||
@ -37,6 +37,7 @@ export class Downloader implements Processor {
|
|||||||
const modelId = array.pop() ?? ''
|
const modelId = array.pop() ?? ''
|
||||||
|
|
||||||
const destination = resolve(getJanDataFolderPath(), normalizedPath)
|
const destination = resolve(getJanDataFolderPath(), normalizedPath)
|
||||||
|
validatePath(destination)
|
||||||
const rq = request({ url, strictSSL, proxy })
|
const rq = request({ url, strictSSL, proxy })
|
||||||
|
|
||||||
// Put request to download manager instance
|
// Put request to download manager instance
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { join } from 'path'
|
import { join, resolve } from 'path'
|
||||||
import { normalizeFilePath } from '../../helper/path'
|
import { normalizeFilePath, validatePath } from '../../helper/path'
|
||||||
import { getJanDataFolderPath } from '../../helper'
|
import { getJanDataFolderPath } from '../../helper'
|
||||||
import { Processor } from './Processor'
|
import { Processor } from './Processor'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
@ -15,17 +15,29 @@ export class FileSystem implements Processor {
|
|||||||
process(route: string, ...args: any): any {
|
process(route: string, ...args: any): any {
|
||||||
const instance = this as any
|
const instance = this as any
|
||||||
const func = instance[route]
|
const func = instance[route]
|
||||||
|
|
||||||
if (func) {
|
if (func) {
|
||||||
return func(...args)
|
return func(...args)
|
||||||
} else {
|
} else {
|
||||||
return import(FileSystem.moduleName).then((mdl) =>
|
return import(FileSystem.moduleName).then((mdl) =>
|
||||||
mdl[route](
|
mdl[route](
|
||||||
...args.map((arg: any) => {
|
...args.map((arg: any, index: number) => {
|
||||||
return typeof arg === 'string' &&
|
if(index !== 0) {
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
if (index === 0 && typeof arg !== 'string') {
|
||||||
|
throw new Error(`Invalid argument ${JSON.stringify(args)}`)
|
||||||
|
}
|
||||||
|
const path =
|
||||||
(arg.startsWith(`file:/`) || arg.startsWith(`file:\\`))
|
(arg.startsWith(`file:/`) || arg.startsWith(`file:\\`))
|
||||||
? join(getJanDataFolderPath(), normalizeFilePath(arg))
|
? join(getJanDataFolderPath(), normalizeFilePath(arg))
|
||||||
: arg
|
: arg
|
||||||
|
|
||||||
|
if(path.startsWith(`http://`) || path.startsWith(`https://`)) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
const absolutePath = resolve(path)
|
||||||
|
validatePath(absolutePath)
|
||||||
|
return absolutePath
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -42,8 +54,11 @@ export class FileSystem implements Processor {
|
|||||||
path = join(getJanDataFolderPath(), normalizeFilePath(path))
|
path = join(getJanDataFolderPath(), normalizeFilePath(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const absolutePath = resolve(path)
|
||||||
|
validatePath(absolutePath)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.rm(path, { recursive: true, force: true }, (err) => {
|
fs.rm(absolutePath, { recursive: true, force: true }, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err)
|
reject(err)
|
||||||
} else {
|
} else {
|
||||||
@ -63,8 +78,11 @@ export class FileSystem implements Processor {
|
|||||||
path = join(getJanDataFolderPath(), normalizeFilePath(path))
|
path = join(getJanDataFolderPath(), normalizeFilePath(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const absolutePath = resolve(path)
|
||||||
|
validatePath(absolutePath)
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.mkdir(path, { recursive: true }, (err) => {
|
fs.mkdir(absolutePath, { recursive: true }, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err)
|
reject(err)
|
||||||
} else {
|
} else {
|
||||||
@ -73,4 +91,5 @@ export class FileSystem implements Processor {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import { appResourcePath, normalizeFilePath } from '../../helper/path'
|
import { appResourcePath, normalizeFilePath, validatePath } from '../../helper/path'
|
||||||
import { getJanDataFolderPath, getJanDataFolderPath as getPath } from '../../helper'
|
import { getJanDataFolderPath, getJanDataFolderPath as getPath } from '../../helper'
|
||||||
import { Processor } from './Processor'
|
import { Processor } from './Processor'
|
||||||
import { FileStat } from '../../../types'
|
import { FileStat } from '../../../types'
|
||||||
@ -21,6 +21,7 @@ export class FSExt implements Processor {
|
|||||||
// Handles the 'syncFile' IPC event. This event is triggered to synchronize a file from a source path to a destination path.
|
// Handles the 'syncFile' IPC event. This event is triggered to synchronize a file from a source path to a destination path.
|
||||||
syncFile(src: string, dest: string) {
|
syncFile(src: string, dest: string) {
|
||||||
const reflect = require('@alumna/reflect')
|
const reflect = require('@alumna/reflect')
|
||||||
|
validatePath(dest)
|
||||||
return reflect({
|
return reflect({
|
||||||
src,
|
src,
|
||||||
dest,
|
dest,
|
||||||
@ -70,14 +71,18 @@ export class FSExt implements Processor {
|
|||||||
writeBlob(path: string, data: any) {
|
writeBlob(path: string, data: any) {
|
||||||
try {
|
try {
|
||||||
const normalizedPath = normalizeFilePath(path)
|
const normalizedPath = normalizeFilePath(path)
|
||||||
|
|
||||||
const dataBuffer = Buffer.from(data, 'base64')
|
const dataBuffer = Buffer.from(data, 'base64')
|
||||||
fs.writeFileSync(join(getJanDataFolderPath(), normalizedPath), dataBuffer)
|
const writePath = join(getJanDataFolderPath(), normalizedPath)
|
||||||
|
validatePath(writePath)
|
||||||
|
fs.writeFileSync(writePath, dataBuffer)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`writeFile ${path} result: ${err}`)
|
console.error(`writeFile ${path} result: ${err}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
copyFile(src: string, dest: string): Promise<void> {
|
copyFile(src: string, dest: string): Promise<void> {
|
||||||
|
validatePath(dest)
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.copyFile(src, dest, (err) => {
|
fs.copyFile(src, dest, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@ -7,7 +7,8 @@ import childProcess from 'child_process'
|
|||||||
const configurationFileName = 'settings.json'
|
const configurationFileName = 'settings.json'
|
||||||
|
|
||||||
// TODO: do no specify app name in framework module
|
// TODO: do no specify app name in framework module
|
||||||
const defaultJanDataFolder = join(os.homedir(), 'jan')
|
// TODO: do not default the os.homedir
|
||||||
|
const defaultJanDataFolder = join(os?.homedir() || '', 'jan')
|
||||||
const defaultAppConfig: AppConfiguration = {
|
const defaultAppConfig: AppConfiguration = {
|
||||||
data_folder: defaultJanDataFolder,
|
data_folder: defaultJanDataFolder,
|
||||||
quick_ask: false,
|
quick_ask: false,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { join } from 'path'
|
import { join, resolve } from 'path'
|
||||||
|
import { getJanDataFolderPath } from './config'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize file path
|
* Normalize file path
|
||||||
@ -33,3 +34,11 @@ export async function appResourcePath(): Promise<string> {
|
|||||||
// server
|
// server
|
||||||
return join(global.core.appPath(), '../../..')
|
return join(global.core.appPath(), '../../..')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function validatePath(path: string) {
|
||||||
|
const janDataFolderPath = getJanDataFolderPath()
|
||||||
|
const absolutePath = resolve(__dirname, path)
|
||||||
|
if (!absolutePath.startsWith(janDataFolderPath)) {
|
||||||
|
throw new Error(`Invalid path: ${absolutePath}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -90,7 +90,6 @@ export enum ExtensionRoute {
|
|||||||
}
|
}
|
||||||
export enum FileSystemRoute {
|
export enum FileSystemRoute {
|
||||||
appendFileSync = 'appendFileSync',
|
appendFileSync = 'appendFileSync',
|
||||||
copyFileSync = 'copyFileSync',
|
|
||||||
unlinkSync = 'unlinkSync',
|
unlinkSync = 'unlinkSync',
|
||||||
existsSync = 'existsSync',
|
existsSync = 'existsSync',
|
||||||
readdirSync = 'readdirSync',
|
readdirSync = 'readdirSync',
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@janhq/model-extension",
|
"name": "@janhq/model-extension",
|
||||||
"productName": "Model Management",
|
"productName": "Model Management",
|
||||||
"version": "1.0.32",
|
"version": "1.0.33",
|
||||||
"description": "Model Management Extension provides model exploration and seamless downloads",
|
"description": "Model Management Extension provides model exploration and seamless downloads",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"node": "dist/node/index.cjs.js",
|
"node": "dist/node/index.cjs.js",
|
||||||
|
|||||||
@ -383,7 +383,12 @@ export default class JanModelExtension extends ModelExtension {
|
|||||||
|
|
||||||
// model binaries (sources) are absolute path & exist
|
// model binaries (sources) are absolute path & exist
|
||||||
const existFiles = await Promise.all(
|
const existFiles = await Promise.all(
|
||||||
model.sources.map((source) => fs.existsSync(source.url))
|
model.sources.map(
|
||||||
|
(source) =>
|
||||||
|
// Supposed to be a local file url
|
||||||
|
!source.url.startsWith(`http://`) &&
|
||||||
|
!source.url.startsWith(`https://`)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if (existFiles.every((exist) => exist)) return true
|
if (existFiles.every((exist) => exist)) return true
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user