mirror of
https://github.com/whyour/qinglong.git
synced 2026-08-11 19:05:20 +08:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc92ba47f6 | ||
|
|
dfb70abbe3 |
+5
-34
@@ -13,29 +13,9 @@ import { isValidToken } from '../shared/auth';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
export default ({ app }: { app: Application }) => {
|
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.set('trust proxy', 'loopback');
|
||||||
app.use(cors());
|
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
|
// Rewrite URLs to strip baseUrl prefix if configured
|
||||||
// This allows the rest of the app to work without baseUrl awareness
|
// This allows the rest of the app to work without baseUrl awareness
|
||||||
if (config.baseUrl) {
|
if (config.baseUrl) {
|
||||||
@@ -56,7 +36,7 @@ export default ({ app }: { app: Application }) => {
|
|||||||
secret: config.jwt.secret,
|
secret: config.jwt.secret,
|
||||||
algorithms: ['HS384'],
|
algorithms: ['HS384'],
|
||||||
}).unless({
|
}).unless({
|
||||||
path: [...config.apiWhiteList, /^(\/(?!api\/).*)$/i],
|
path: [...config.apiWhiteList, /^\/(?!api\/).*/],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -71,20 +51,19 @@ export default ({ app }: { app: Application }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.use(async (req: Request, res, next) => {
|
app.use(async (req: Request, res, next) => {
|
||||||
const pathLower = req.path.toLowerCase();
|
if (!['/open/', '/api/'].some((x) => req.path.startsWith(x))) {
|
||||||
if (!['/open/', '/api/'].some((x) => pathLower.startsWith(x))) {
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerToken = getToken(req);
|
const headerToken = getToken(req);
|
||||||
if (pathLower.startsWith('/open/')) {
|
if (req.path.startsWith('/open/')) {
|
||||||
const apps = await shareStore.getApps();
|
const apps = await shareStore.getApps();
|
||||||
const doc = apps?.filter((x) =>
|
const doc = apps?.filter((x) =>
|
||||||
x.tokens?.find((y) => y.value === headerToken),
|
x.tokens?.find((y) => y.value === headerToken),
|
||||||
)?.[0];
|
)?.[0];
|
||||||
if (doc && doc.tokens && doc.tokens.length > 0) {
|
if (doc && doc.tokens && doc.tokens.length > 0) {
|
||||||
const currentToken = doc.tokens.find((x) => x.value === headerToken);
|
const currentToken = doc.tokens.find((x) => x.value === headerToken);
|
||||||
const keyMatch = pathLower.match(/\/open\/([a-z]+)\/*/);
|
const keyMatch = req.path.match(/\/open\/([a-z]+)\/*/);
|
||||||
const key = keyMatch && keyMatch[1];
|
const key = keyMatch && keyMatch[1];
|
||||||
if (
|
if (
|
||||||
doc.scopes.includes(key as any) &&
|
doc.scopes.includes(key as any) &&
|
||||||
@@ -119,15 +98,7 @@ export default ({ app }: { app: Application }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.use(async (req, res, next) => {
|
app.use(async (req, res, next) => {
|
||||||
const pathLower = req.path.toLowerCase();
|
if (!['/api/user/init', '/api/user/notification/init'].includes(req.path)) {
|
||||||
if (
|
|
||||||
![
|
|
||||||
'/api/user/init',
|
|
||||||
'/api/user/notification/init',
|
|
||||||
'/open/user/init',
|
|
||||||
'/open/user/notification/init',
|
|
||||||
].includes(req.path)
|
|
||||||
) {
|
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
const authInfo =
|
const authInfo =
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class GrpcServerService {
|
|||||||
const grpcPort = config.grpcPort;
|
const grpcPort = config.grpcPort;
|
||||||
const bindAsync = promisify(this.server.bindAsync).bind(this.server);
|
const bindAsync = promisify(this.server.bindAsync).bind(this.server);
|
||||||
await bindAsync(
|
await bindAsync(
|
||||||
`0.0.0.0:${grpcPort}`,
|
`[::]:${grpcPort}`,
|
||||||
ServerCredentials.createInsecure(),
|
ServerCredentials.createInsecure(),
|
||||||
);
|
);
|
||||||
Logger.debug(`✌️ gRPC service started successfully`);
|
Logger.debug(`✌️ gRPC service started successfully`);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class HttpServerService {
|
|||||||
async initialize(expressApp: express.Application, port: number) {
|
async initialize(expressApp: express.Application, port: number) {
|
||||||
try {
|
try {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.server = expressApp.listen(port, '0.0.0.0', () => {
|
this.server = expressApp.listen(port, '::', () => {
|
||||||
Logger.debug(`✌️ HTTP service started successfully`);
|
Logger.debug(`✌️ HTTP service started successfully`);
|
||||||
metricsService.record('http_service_start', 1, {
|
metricsService.record('http_service_start', 1, {
|
||||||
port: port.toString(),
|
port: port.toString(),
|
||||||
|
|||||||
Reference in New Issue
Block a user