@withwiz/toolkit 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/core/jwt/cookie.d.ts +21 -0
- package/dist/auth/core/jwt/cookie.js +9 -0
- package/dist/auth/core/jwt/index.d.ts +2 -0
- package/dist/auth/core/jwt/index.js +8 -2
- package/dist/auth/index.js +19 -18
- package/dist/{chunk-2ATBAIRL.js → chunk-3GAY2T2A.js} +1 -1
- package/dist/chunk-AJLUPYCQ.js +50 -0
- package/dist/{chunk-JR7GVCQX.js → chunk-BKJPYZYO.js} +10 -7
- package/dist/{chunk-BDY3AOX5.js → chunk-HUDWNKGY.js} +16 -10
- package/dist/{chunk-LD7POV6B.js → chunk-JJ3I57N2.js} +3 -3
- package/dist/{chunk-SR65BF6X.js → chunk-N54KGJPU.js} +9 -1
- package/dist/{chunk-QVWDWROP.js → chunk-WMQE2YK7.js} +58 -0
- package/dist/components/ui/DataTable.js +2 -2
- package/dist/components/ui/data-table/DataTable.js +2 -2
- package/dist/constants/index.js +13 -13
- package/dist/error/error-display.js +2 -2
- package/dist/error/index.js +10 -10
- package/dist/middleware/auth.d.ts +5 -2
- package/dist/middleware/auth.js +3 -2
- package/dist/middleware/error-handler.js +2 -2
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +9 -6
- package/dist/middleware/security.d.ts +4 -0
- package/dist/middleware/security.js +3 -1
- package/dist/middleware/wrappers.js +7 -6
- package/dist/utils/index.js +1 -1
- package/dist/utils/ip-utils.d.ts +9 -6
- package/dist/utils/ip-utils.js +3 -6
- package/dist/utils/sanitizer.d.ts +5 -4
- package/dist/utils/sanitizer.js +1 -1
- package/dist/utils/short-code-generator.d.ts +2 -1
- package/dist/utils/short-code-generator.js +3 -1
- package/dist/utils/url-normalizer.js +22 -1
- package/package.json +1 -1
- package/dist/{chunk-LX2EYD74.js → chunk-HB5GFTFD.js} +3 -3
- package/dist/{chunk-IJCVFLEY.js → chunk-TQJDNWBL.js} +3 -3
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT 토큰을 HttpOnly 쿠키로 설정/삭제하는 유틸리티
|
|
3
|
+
*
|
|
4
|
+
* NextResponse 타입을 제네릭으로 처리하여
|
|
5
|
+
* symlink 환경에서의 next 패키지 경로 충돌을 방지합니다.
|
|
6
|
+
*/
|
|
7
|
+
import type { TokenPair } from '@withwiz/auth/types';
|
|
8
|
+
/** cookies.set()을 지원하는 Response 타입 */
|
|
9
|
+
interface CookieSettableResponse {
|
|
10
|
+
cookies: {
|
|
11
|
+
set(name: string, value: string, options?: Record<string, unknown>): void;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export interface CookieOptions {
|
|
15
|
+
secure?: boolean;
|
|
16
|
+
sameSite?: 'lax' | 'strict' | 'none';
|
|
17
|
+
domain?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function setTokenCookies<T extends CookieSettableResponse>(response: T, tokenPair: TokenPair, options?: CookieOptions): T;
|
|
20
|
+
export declare function clearTokenCookies<T extends CookieSettableResponse>(response: T, options?: CookieOptions): T;
|
|
21
|
+
export {};
|
|
@@ -107,4 +107,6 @@ export declare class JWTService {
|
|
|
107
107
|
*/
|
|
108
108
|
extractTokenFromHeader(authHeader: string | undefined): string | null;
|
|
109
109
|
}
|
|
110
|
+
export { setTokenCookies, clearTokenCookies } from './cookie';
|
|
111
|
+
export type { CookieOptions } from './cookie';
|
|
110
112
|
export type { JWTConfig, JWTPayload, TokenPair };
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
JWTManager,
|
|
3
3
|
JWTService
|
|
4
|
-
} from "../../../chunk-
|
|
4
|
+
} from "../../../chunk-3GAY2T2A.js";
|
|
5
|
+
import {
|
|
6
|
+
clearTokenCookies,
|
|
7
|
+
setTokenCookies
|
|
8
|
+
} from "../../../chunk-AJLUPYCQ.js";
|
|
5
9
|
import "../../../chunk-AIH3F7JV.js";
|
|
6
10
|
import "../../../chunk-ORMEWXMH.js";
|
|
7
11
|
export {
|
|
8
12
|
JWTManager,
|
|
9
|
-
JWTService
|
|
13
|
+
JWTService,
|
|
14
|
+
clearTokenCookies,
|
|
15
|
+
setTokenCookies
|
|
10
16
|
};
|
package/dist/auth/index.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_PASSWORD_CONFIG,
|
|
3
|
+
createPasswordHasher,
|
|
4
|
+
createPasswordSchema,
|
|
5
|
+
createPasswordValidator,
|
|
6
|
+
getPasswordStrength,
|
|
7
|
+
passwordValidator,
|
|
8
|
+
validatePassword
|
|
9
|
+
} from "../chunk-G26T2PRQ.js";
|
|
10
|
+
import {
|
|
11
|
+
PasswordHasher,
|
|
12
|
+
PasswordValidator,
|
|
13
|
+
defaultPasswordSchema,
|
|
14
|
+
strongPasswordSchema
|
|
15
|
+
} from "../chunk-IHXRF3BH.js";
|
|
1
16
|
import {
|
|
2
17
|
TokenGenerator
|
|
3
18
|
} from "../chunk-GDWEDUHO.js";
|
|
4
|
-
import {
|
|
5
|
-
OAuthManager
|
|
6
|
-
} from "../chunk-V5K5FYU7.js";
|
|
7
19
|
import {
|
|
8
20
|
JWTClientManager,
|
|
9
21
|
clearStoredTokens,
|
|
@@ -21,20 +33,8 @@ import {
|
|
|
21
33
|
storeTokens
|
|
22
34
|
} from "../chunk-XRRPEBKB.js";
|
|
23
35
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
createPasswordSchema,
|
|
27
|
-
createPasswordValidator,
|
|
28
|
-
getPasswordStrength,
|
|
29
|
-
passwordValidator,
|
|
30
|
-
validatePassword
|
|
31
|
-
} from "../chunk-G26T2PRQ.js";
|
|
32
|
-
import {
|
|
33
|
-
PasswordHasher,
|
|
34
|
-
PasswordValidator,
|
|
35
|
-
defaultPasswordSchema,
|
|
36
|
-
strongPasswordSchema
|
|
37
|
-
} from "../chunk-IHXRF3BH.js";
|
|
36
|
+
OAuthManager
|
|
37
|
+
} from "../chunk-V5K5FYU7.js";
|
|
38
38
|
import {
|
|
39
39
|
OAuthProvider,
|
|
40
40
|
PasswordStrength,
|
|
@@ -44,7 +44,8 @@ import {
|
|
|
44
44
|
import {
|
|
45
45
|
JWTManager,
|
|
46
46
|
JWTService
|
|
47
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-3GAY2T2A.js";
|
|
48
|
+
import "../chunk-AJLUPYCQ.js";
|
|
48
49
|
import {
|
|
49
50
|
AUTH_ERROR_CODES,
|
|
50
51
|
AuthError,
|
|
@@ -25,7 +25,7 @@ var JWTManager = class {
|
|
|
25
25
|
});
|
|
26
26
|
return jwt;
|
|
27
27
|
} catch (error) {
|
|
28
|
-
this.logger.error("Failed to create access token", { error, payload });
|
|
28
|
+
this.logger.error("Failed to create access token", { error, userId: payload.userId });
|
|
29
29
|
throw new JWTError(
|
|
30
30
|
"Access token creation failed",
|
|
31
31
|
"TOKEN_CREATION_FAILED"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__spreadValues
|
|
3
|
+
} from "./chunk-ORMEWXMH.js";
|
|
4
|
+
|
|
5
|
+
// src/auth/core/jwt/cookie.ts
|
|
6
|
+
var DEFAULT_OPTIONS = {
|
|
7
|
+
secure: process.env.NODE_ENV === "production",
|
|
8
|
+
sameSite: "lax"
|
|
9
|
+
};
|
|
10
|
+
function setTokenCookies(response, tokenPair, options = {}) {
|
|
11
|
+
const opts = __spreadValues(__spreadValues({}, DEFAULT_OPTIONS), options);
|
|
12
|
+
response.cookies.set("access_token", tokenPair.accessToken, {
|
|
13
|
+
httpOnly: true,
|
|
14
|
+
secure: opts.secure,
|
|
15
|
+
sameSite: opts.sameSite,
|
|
16
|
+
path: "/",
|
|
17
|
+
maxAge: 15 * 60
|
|
18
|
+
});
|
|
19
|
+
response.cookies.set("refresh_token", tokenPair.refreshToken, {
|
|
20
|
+
httpOnly: true,
|
|
21
|
+
secure: opts.secure,
|
|
22
|
+
sameSite: opts.sameSite,
|
|
23
|
+
path: "/api/auth",
|
|
24
|
+
maxAge: 7 * 24 * 60 * 60
|
|
25
|
+
});
|
|
26
|
+
return response;
|
|
27
|
+
}
|
|
28
|
+
function clearTokenCookies(response, options = {}) {
|
|
29
|
+
const opts = __spreadValues(__spreadValues({}, DEFAULT_OPTIONS), options);
|
|
30
|
+
response.cookies.set("access_token", "", {
|
|
31
|
+
httpOnly: true,
|
|
32
|
+
secure: opts.secure,
|
|
33
|
+
sameSite: opts.sameSite,
|
|
34
|
+
path: "/",
|
|
35
|
+
maxAge: 0
|
|
36
|
+
});
|
|
37
|
+
response.cookies.set("refresh_token", "", {
|
|
38
|
+
httpOnly: true,
|
|
39
|
+
secure: opts.secure,
|
|
40
|
+
sameSite: opts.sameSite,
|
|
41
|
+
path: "/api/auth",
|
|
42
|
+
maxAge: 0
|
|
43
|
+
});
|
|
44
|
+
return response;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
setTokenCookies,
|
|
49
|
+
clearTokenCookies
|
|
50
|
+
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AUTH_ERROR_CODE_MAP
|
|
3
3
|
} from "./chunk-2KDLKHTO.js";
|
|
4
|
-
import {
|
|
5
|
-
getErrorMessage
|
|
6
|
-
} from "./chunk-Y2JYA2AR.js";
|
|
7
4
|
import {
|
|
8
5
|
AppError
|
|
9
6
|
} from "./chunk-KXXROUA5.js";
|
|
7
|
+
import {
|
|
8
|
+
getErrorMessage
|
|
9
|
+
} from "./chunk-Y2JYA2AR.js";
|
|
10
10
|
import {
|
|
11
11
|
ERROR_CODES,
|
|
12
12
|
classifyError
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
import { NextResponse } from "next/server";
|
|
29
29
|
import { z } from "zod";
|
|
30
30
|
var errorHandlerMiddleware = async (context, next) => {
|
|
31
|
-
var _a
|
|
31
|
+
var _a;
|
|
32
32
|
try {
|
|
33
33
|
return await next();
|
|
34
34
|
} catch (error) {
|
|
@@ -75,7 +75,11 @@ var errorHandlerMiddleware = async (context, next) => {
|
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
const classified = error instanceof Error ? classifyError(error) : ERROR_CODES.INTERNAL_SERVER_ERROR;
|
|
78
|
-
logger.error("[ErrorHandler] Unexpected error:",
|
|
78
|
+
logger.error("[ErrorHandler] Unexpected error:", {
|
|
79
|
+
error,
|
|
80
|
+
requestId: context.requestId,
|
|
81
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
82
|
+
});
|
|
79
83
|
let isProduction = false;
|
|
80
84
|
try {
|
|
81
85
|
isProduction = getCommonConfig().nodeEnv === "production";
|
|
@@ -87,8 +91,7 @@ var errorHandlerMiddleware = async (context, next) => {
|
|
|
87
91
|
classified.code,
|
|
88
92
|
safeMessage,
|
|
89
93
|
context.locale,
|
|
90
|
-
context.requestId
|
|
91
|
-
!isProduction && error instanceof Error ? { stack: (_b = error.stack) == null ? void 0 : _b.split("\n").slice(0, 5) } : void 0
|
|
94
|
+
context.requestId
|
|
92
95
|
);
|
|
93
96
|
}
|
|
94
97
|
};
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "./chunk-OZE5KUS3.js";
|
|
10
10
|
import {
|
|
11
11
|
JWTManager
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-3GAY2T2A.js";
|
|
13
13
|
import {
|
|
14
14
|
logger
|
|
15
15
|
} from "./chunk-GQVBWZLH.js";
|
|
@@ -72,7 +72,7 @@ function initializeAuthMiddleware() {
|
|
|
72
72
|
return jwtManager !== null;
|
|
73
73
|
}
|
|
74
74
|
var authMiddleware = async (context, next) => {
|
|
75
|
-
var _a, _b;
|
|
75
|
+
var _a, _b, _c, _d;
|
|
76
76
|
try {
|
|
77
77
|
const jwtManager = getJWTManager();
|
|
78
78
|
if (!jwtManager) {
|
|
@@ -84,8 +84,11 @@ var authMiddleware = async (context, next) => {
|
|
|
84
84
|
"Authentication not configured. Contact administrator."
|
|
85
85
|
);
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
let token = (_b = (_a = context.request.cookies.get("access_token")) == null ? void 0 : _a.value) != null ? _b : null;
|
|
88
|
+
if (!token) {
|
|
89
|
+
const authHeader = context.request.headers.get("authorization");
|
|
90
|
+
token = jwtManager.extractTokenFromHeader(authHeader);
|
|
91
|
+
}
|
|
89
92
|
if (!token) {
|
|
90
93
|
throw new AppError(ERROR_CODES.UNAUTHORIZED.code);
|
|
91
94
|
}
|
|
@@ -105,7 +108,7 @@ var authMiddleware = async (context, next) => {
|
|
|
105
108
|
const payload = await jwtManager.verifyAccessToken(token);
|
|
106
109
|
context.user = {
|
|
107
110
|
id: payload.userId,
|
|
108
|
-
email: (
|
|
111
|
+
email: (_c = payload.email) != null ? _c : "",
|
|
109
112
|
name: void 0,
|
|
110
113
|
// 필요시 DB에서 조회
|
|
111
114
|
role: payload.role === "ADMIN" ? "ADMIN" : "USER"
|
|
@@ -114,7 +117,7 @@ var authMiddleware = async (context, next) => {
|
|
|
114
117
|
if (error instanceof AppError) {
|
|
115
118
|
throw error;
|
|
116
119
|
}
|
|
117
|
-
if (error.code === "TOKEN_EXPIRED" || ((
|
|
120
|
+
if (error.code === "TOKEN_EXPIRED" || ((_d = error.message) == null ? void 0 : _d.includes("expired"))) {
|
|
118
121
|
throw new AppError(ERROR_CODES.TOKEN_EXPIRED.code, error.message);
|
|
119
122
|
}
|
|
120
123
|
throw new AppError(
|
|
@@ -125,12 +128,15 @@ var authMiddleware = async (context, next) => {
|
|
|
125
128
|
return await next();
|
|
126
129
|
};
|
|
127
130
|
var optionalAuthMiddleware = async (context, next) => {
|
|
128
|
-
var _a;
|
|
131
|
+
var _a, _b, _c;
|
|
129
132
|
try {
|
|
130
133
|
const jwtManager = getJWTManager();
|
|
131
134
|
if (jwtManager) {
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
let token = (_b = (_a = context.request.cookies.get("access_token")) == null ? void 0 : _a.value) != null ? _b : null;
|
|
136
|
+
if (!token) {
|
|
137
|
+
const authHeader = context.request.headers.get("authorization");
|
|
138
|
+
token = jwtManager.extractTokenFromHeader(authHeader);
|
|
139
|
+
}
|
|
134
140
|
if (token) {
|
|
135
141
|
const tokenChecker = getAccessTokenChecker();
|
|
136
142
|
let isRevoked = false;
|
|
@@ -141,7 +147,7 @@ var optionalAuthMiddleware = async (context, next) => {
|
|
|
141
147
|
const payload = await jwtManager.verifyAccessToken(token);
|
|
142
148
|
context.user = {
|
|
143
149
|
id: payload.userId,
|
|
144
|
-
email: (
|
|
150
|
+
email: (_c = payload.email) != null ? _c : "",
|
|
145
151
|
name: void 0,
|
|
146
152
|
role: payload.role === "ADMIN" ? "ADMIN" : "USER"
|
|
147
153
|
};
|
|
@@ -12,18 +12,18 @@ import {
|
|
|
12
12
|
} from "./chunk-OIRAH57Y.js";
|
|
13
13
|
import {
|
|
14
14
|
securityMiddleware
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WMQE2YK7.js";
|
|
16
16
|
import {
|
|
17
17
|
adminMiddleware,
|
|
18
18
|
authMiddleware,
|
|
19
19
|
optionalAuthMiddleware
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-HUDWNKGY.js";
|
|
21
21
|
import {
|
|
22
22
|
corsMiddleware
|
|
23
23
|
} from "./chunk-KGHWGLED.js";
|
|
24
24
|
import {
|
|
25
25
|
errorHandlerMiddleware
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-BKJPYZYO.js";
|
|
27
27
|
|
|
28
28
|
// src/middleware/wrappers.ts
|
|
29
29
|
function withPublicApi(handler) {
|
|
@@ -7,7 +7,15 @@ function sanitizeHtml(input) {
|
|
|
7
7
|
if (!input || typeof input !== "string") {
|
|
8
8
|
return "";
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
let result = input;
|
|
11
|
+
result = result.replace(/<[^>]*>/g, "");
|
|
12
|
+
result = result.replace(/</gi, "<").replace(/>/gi, ">");
|
|
13
|
+
result = result.replace(/<[^>]*>/g, "");
|
|
14
|
+
result = result.replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(Number(dec))).replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
15
|
+
result = result.replace(/<[^>]*>/g, "");
|
|
16
|
+
result = result.replace(/<[^>]*$/g, "");
|
|
17
|
+
result = result.replace(/&/gi, "&").replace(/"/gi, '"').replace(/'/gi, "'").replace(///gi, "/");
|
|
18
|
+
return result.trim();
|
|
11
19
|
}
|
|
12
20
|
function removeEventHandlers(input) {
|
|
13
21
|
if (!input || typeof input !== "string") {
|
|
@@ -4,6 +4,35 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// src/middleware/security.ts
|
|
6
6
|
import { NextResponse } from "next/server";
|
|
7
|
+
function setAllowedOrigins(origins) {
|
|
8
|
+
globalThis.__withwiz_allowed_origins = origins;
|
|
9
|
+
logger.info(`[Security Middleware] Allowed origins configured: ${origins.join(", ")}`);
|
|
10
|
+
}
|
|
11
|
+
var STATE_CHANGING_METHODS = ["POST", "PUT", "PATCH", "DELETE"];
|
|
12
|
+
function verifyOrigin(request) {
|
|
13
|
+
const allowedOrigins = globalThis.__withwiz_allowed_origins;
|
|
14
|
+
if (!allowedOrigins || allowedOrigins.length === 0) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
const method = request.method.toUpperCase();
|
|
18
|
+
if (!STATE_CHANGING_METHODS.includes(method)) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
const origin = request.headers.get("origin");
|
|
22
|
+
if (origin) {
|
|
23
|
+
return allowedOrigins.some((allowed) => origin === allowed);
|
|
24
|
+
}
|
|
25
|
+
const referer = request.headers.get("referer");
|
|
26
|
+
if (referer) {
|
|
27
|
+
try {
|
|
28
|
+
const refererOrigin = new URL(referer).origin;
|
|
29
|
+
return allowedOrigins.some((allowed) => refererOrigin === allowed);
|
|
30
|
+
} catch (e) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
7
36
|
var BLOCKED_METHODS = ["TRACE", "TRACK"];
|
|
8
37
|
var ALLOWED_CONTENT_TYPES = [
|
|
9
38
|
"application/json",
|
|
@@ -97,6 +126,32 @@ var securityMiddleware = async (context, next) => {
|
|
|
97
126
|
}
|
|
98
127
|
}
|
|
99
128
|
}
|
|
129
|
+
if (!verifyOrigin(request)) {
|
|
130
|
+
logger.warn("[Security] Origin verification failed", {
|
|
131
|
+
method,
|
|
132
|
+
origin: request.headers.get("origin"),
|
|
133
|
+
referer: request.headers.get("referer"),
|
|
134
|
+
url: request.url
|
|
135
|
+
});
|
|
136
|
+
return new NextResponse(
|
|
137
|
+
JSON.stringify({
|
|
138
|
+
success: false,
|
|
139
|
+
error: {
|
|
140
|
+
code: 40300,
|
|
141
|
+
message: "Origin verification failed",
|
|
142
|
+
userMessage: {
|
|
143
|
+
title: "\uC694\uCCAD \uAC70\uBD80",
|
|
144
|
+
description: "\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC740 \uCD9C\uCC98\uC5D0\uC11C\uC758 \uC694\uCCAD\uC785\uB2C8\uB2E4.",
|
|
145
|
+
action: "\uC62C\uBC14\uB978 \uD398\uC774\uC9C0\uC5D0\uC11C \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694."
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}),
|
|
149
|
+
{
|
|
150
|
+
status: 403,
|
|
151
|
+
headers: { "Content-Type": "application/json" }
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
}
|
|
100
155
|
const response = await next();
|
|
101
156
|
if (!response.headers.has("X-Content-Type-Options")) {
|
|
102
157
|
response.headers.set("X-Content-Type-Options", "nosniff");
|
|
@@ -118,9 +173,12 @@ var securityMiddleware = async (context, next) => {
|
|
|
118
173
|
function validateSecurityConfiguration() {
|
|
119
174
|
logger.info("[Security Middleware] Blocked methods: " + BLOCKED_METHODS.join(", "));
|
|
120
175
|
logger.info("[Security Middleware] Allowed Content-Types: " + ALLOWED_CONTENT_TYPES.join(", "));
|
|
176
|
+
const origins = globalThis.__withwiz_allowed_origins;
|
|
177
|
+
logger.info("[Security Middleware] Allowed Origins: " + ((origins == null ? void 0 : origins.join(", ")) || "NOT CONFIGURED (origin check disabled)"));
|
|
121
178
|
}
|
|
122
179
|
|
|
123
180
|
export {
|
|
181
|
+
setAllowedOrigins,
|
|
124
182
|
securityMiddleware,
|
|
125
183
|
validateSecurityConfiguration
|
|
126
184
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DataTable
|
|
3
|
-
} from "../../chunk-
|
|
4
|
-
import "../../chunk-7IY3RQQL.js";
|
|
3
|
+
} from "../../chunk-HB5GFTFD.js";
|
|
5
4
|
import "../../chunk-MAATEX2R.js";
|
|
6
5
|
import "../../chunk-SEZJN4TC.js";
|
|
7
6
|
import "../../chunk-IPXPCBDO.js";
|
|
8
7
|
import "../../chunk-KHYY4KCV.js";
|
|
9
8
|
import "../../chunk-NY5QXT33.js";
|
|
9
|
+
import "../../chunk-7IY3RQQL.js";
|
|
10
10
|
import "../../chunk-34WAGUT5.js";
|
|
11
11
|
import "../../chunk-RJUVBBZG.js";
|
|
12
12
|
import "../../chunk-IJEZ7G7S.js";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
DataTable
|
|
4
|
-
} from "../../../chunk-
|
|
5
|
-
import "../../../chunk-7IY3RQQL.js";
|
|
4
|
+
} from "../../../chunk-HB5GFTFD.js";
|
|
6
5
|
import "../../../chunk-MAATEX2R.js";
|
|
7
6
|
import "../../../chunk-SEZJN4TC.js";
|
|
8
7
|
import "../../../chunk-IPXPCBDO.js";
|
|
9
8
|
import "../../../chunk-KHYY4KCV.js";
|
|
10
9
|
import "../../../chunk-NY5QXT33.js";
|
|
10
|
+
import "../../../chunk-7IY3RQQL.js";
|
|
11
11
|
import "../../../chunk-34WAGUT5.js";
|
|
12
12
|
import "../../../chunk-RJUVBBZG.js";
|
|
13
13
|
import "../../../chunk-IJEZ7G7S.js";
|
package/dist/constants/index.js
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GENERIC_CONFIRM_MESSAGES,
|
|
3
|
+
GENERIC_ERROR_MESSAGES,
|
|
4
|
+
GENERIC_INFO_MESSAGES,
|
|
5
|
+
GENERIC_SUCCESS_MESSAGES
|
|
6
|
+
} from "../chunk-2TO7WUKK.js";
|
|
7
|
+
import {
|
|
8
|
+
DATE_RANGE,
|
|
9
|
+
FILTER_OPTIONS,
|
|
10
|
+
PAGE_SIZES,
|
|
11
|
+
PAGINATION,
|
|
12
|
+
SORT_OPTIONS
|
|
13
|
+
} from "../chunk-VDXB5DCY.js";
|
|
1
14
|
import {
|
|
2
15
|
API_KEY,
|
|
3
16
|
CSRF,
|
|
@@ -18,19 +31,6 @@ import {
|
|
|
18
31
|
URL,
|
|
19
32
|
USER_INPUT
|
|
20
33
|
} from "../chunk-LNV2E4I6.js";
|
|
21
|
-
import {
|
|
22
|
-
GENERIC_CONFIRM_MESSAGES,
|
|
23
|
-
GENERIC_ERROR_MESSAGES,
|
|
24
|
-
GENERIC_INFO_MESSAGES,
|
|
25
|
-
GENERIC_SUCCESS_MESSAGES
|
|
26
|
-
} from "../chunk-2TO7WUKK.js";
|
|
27
|
-
import {
|
|
28
|
-
DATE_RANGE,
|
|
29
|
-
FILTER_OPTIONS,
|
|
30
|
-
PAGE_SIZES,
|
|
31
|
-
PAGINATION,
|
|
32
|
-
SORT_OPTIONS
|
|
33
|
-
} from "../chunk-VDXB5DCY.js";
|
|
34
34
|
import {
|
|
35
35
|
ERROR_CODES,
|
|
36
36
|
HTTP_STATUS,
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
getErrorIcon,
|
|
6
6
|
handleApiResponse,
|
|
7
7
|
showFriendlyError
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-TQJDNWBL.js";
|
|
9
|
+
import "../chunk-KXXROUA5.js";
|
|
9
10
|
import {
|
|
10
11
|
getErrorDisplayInfo,
|
|
11
12
|
getFriendlyMessage
|
|
@@ -13,7 +14,6 @@ import {
|
|
|
13
14
|
import "../chunk-Y2JYA2AR.js";
|
|
14
15
|
import "../chunk-HCF3C3AQ.js";
|
|
15
16
|
import "../chunk-IYXMGIEP.js";
|
|
16
|
-
import "../chunk-KXXROUA5.js";
|
|
17
17
|
import "../chunk-SNVGJPPX.js";
|
|
18
18
|
import "../chunk-ORMEWXMH.js";
|
|
19
19
|
export {
|
package/dist/error/index.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
LocaleDetector
|
|
3
3
|
} from "../chunk-XHZ5L4FO.js";
|
|
4
|
+
import {
|
|
5
|
+
extractErrorInfo,
|
|
6
|
+
formatInlineError,
|
|
7
|
+
getDefaultErrorCode,
|
|
8
|
+
getErrorIcon,
|
|
9
|
+
handleApiResponse,
|
|
10
|
+
showFriendlyError
|
|
11
|
+
} from "../chunk-TQJDNWBL.js";
|
|
4
12
|
import {
|
|
5
13
|
AUTH_ERROR_CODE_MAP,
|
|
6
14
|
ErrorResponse,
|
|
@@ -9,13 +17,8 @@ import {
|
|
|
9
17
|
withErrorHandler
|
|
10
18
|
} from "../chunk-2KDLKHTO.js";
|
|
11
19
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
getDefaultErrorCode,
|
|
15
|
-
getErrorIcon,
|
|
16
|
-
handleApiResponse,
|
|
17
|
-
showFriendlyError
|
|
18
|
-
} from "../chunk-IJCVFLEY.js";
|
|
20
|
+
AppError
|
|
21
|
+
} from "../chunk-KXXROUA5.js";
|
|
19
22
|
import {
|
|
20
23
|
formatFriendlyError,
|
|
21
24
|
getErrorDisplayInfo,
|
|
@@ -30,9 +33,6 @@ import {
|
|
|
30
33
|
} from "../chunk-Y2JYA2AR.js";
|
|
31
34
|
import "../chunk-HCF3C3AQ.js";
|
|
32
35
|
import "../chunk-IYXMGIEP.js";
|
|
33
|
-
import {
|
|
34
|
-
AppError
|
|
35
|
-
} from "../chunk-KXXROUA5.js";
|
|
36
36
|
import {
|
|
37
37
|
ERROR_CODES,
|
|
38
38
|
HTTP_STATUS,
|
|
@@ -46,7 +46,8 @@ export declare function initializeAuthMiddleware(): boolean;
|
|
|
46
46
|
/**
|
|
47
47
|
* 인증 미들웨어
|
|
48
48
|
*
|
|
49
|
-
* Authorization 헤더에서 JWT 토큰을 추출하고 검증합니다.
|
|
49
|
+
* 쿠키(access_token) 또는 Authorization 헤더에서 JWT 토큰을 추출하고 검증합니다.
|
|
50
|
+
* 쿠키를 우선 확인하며, 없을 경우 Authorization 헤더로 폴백합니다 (OAPI 호환).
|
|
50
51
|
* 검증된 사용자 정보를 context.user에 추가합니다.
|
|
51
52
|
*
|
|
52
53
|
* @example
|
|
@@ -59,7 +60,9 @@ export declare const authMiddleware: TApiMiddleware;
|
|
|
59
60
|
/**
|
|
60
61
|
* 선택적 인증 미들웨어
|
|
61
62
|
*
|
|
62
|
-
* Authorization 헤더가 있으면 JWT 토큰을 검증하고
|
|
63
|
+
* 쿠키(access_token) 또는 Authorization 헤더가 있으면 JWT 토큰을 검증하고
|
|
64
|
+
* context.user를 설정합니다. 쿠키를 우선 확인하며, 없을 경우 Authorization
|
|
65
|
+
* 헤더로 폴백합니다 (OAPI 호환).
|
|
63
66
|
* 토큰이 없거나 유효하지 않아도 에러를 발생시키지 않고 계속 진행합니다.
|
|
64
67
|
* 공개 API이지만 로그인 사용자를 선택적으로 인식해야 하는 경우에 사용합니다.
|
|
65
68
|
*
|
package/dist/middleware/auth.js
CHANGED
|
@@ -4,11 +4,12 @@ import {
|
|
|
4
4
|
initializeAuthMiddleware,
|
|
5
5
|
optionalAuthMiddleware,
|
|
6
6
|
setAccessTokenBlacklistChecker
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-HUDWNKGY.js";
|
|
8
8
|
import "../chunk-KXXROUA5.js";
|
|
9
9
|
import "../chunk-SNVGJPPX.js";
|
|
10
10
|
import "../chunk-OZE5KUS3.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-3GAY2T2A.js";
|
|
12
|
+
import "../chunk-AJLUPYCQ.js";
|
|
12
13
|
import "../chunk-AIH3F7JV.js";
|
|
13
14
|
import "../chunk-GQVBWZLH.js";
|
|
14
15
|
import "../chunk-OHBAELPZ.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
errorHandlerMiddleware
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-BKJPYZYO.js";
|
|
4
4
|
import "../chunk-2KDLKHTO.js";
|
|
5
|
+
import "../chunk-KXXROUA5.js";
|
|
5
6
|
import "../chunk-Y2JYA2AR.js";
|
|
6
7
|
import "../chunk-HCF3C3AQ.js";
|
|
7
8
|
import "../chunk-IYXMGIEP.js";
|
|
8
|
-
import "../chunk-KXXROUA5.js";
|
|
9
9
|
import "../chunk-SNVGJPPX.js";
|
|
10
10
|
import "../chunk-AIH3F7JV.js";
|
|
11
11
|
import "../chunk-GQVBWZLH.js";
|
|
@@ -12,5 +12,5 @@ export { rateLimitMiddleware, createRateLimitMiddleware, setRateLimitAdapter } f
|
|
|
12
12
|
export { errorHandlerMiddleware } from './error-handler';
|
|
13
13
|
export { responseLoggerMiddleware } from './response-logger';
|
|
14
14
|
export { corsMiddleware, validateCorsConfiguration } from './cors';
|
|
15
|
-
export { securityMiddleware, validateSecurityConfiguration } from './security';
|
|
15
|
+
export { securityMiddleware, validateSecurityConfiguration, setAllowedOrigins } from './security';
|
|
16
16
|
export { withPublicApi, withAuthApi, withAdminApi, withOptionalAuthApi, withCustomApi, } from './wrappers';
|
package/dist/middleware/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
withCustomApi,
|
|
5
5
|
withOptionalAuthApi,
|
|
6
6
|
withPublicApi
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-JJ3I57N2.js";
|
|
8
8
|
import {
|
|
9
9
|
initRequestMiddleware
|
|
10
10
|
} from "../chunk-62Q7DN5G.js";
|
|
@@ -21,32 +21,34 @@ import {
|
|
|
21
21
|
} from "../chunk-OIRAH57Y.js";
|
|
22
22
|
import {
|
|
23
23
|
securityMiddleware,
|
|
24
|
+
setAllowedOrigins,
|
|
24
25
|
validateSecurityConfiguration
|
|
25
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-WMQE2YK7.js";
|
|
26
27
|
import {
|
|
27
28
|
adminMiddleware,
|
|
28
29
|
authMiddleware,
|
|
29
30
|
initializeAuthMiddleware,
|
|
30
31
|
optionalAuthMiddleware,
|
|
31
32
|
setAccessTokenBlacklistChecker
|
|
32
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-HUDWNKGY.js";
|
|
33
34
|
import {
|
|
34
35
|
corsMiddleware,
|
|
35
36
|
validateCorsConfiguration
|
|
36
37
|
} from "../chunk-KGHWGLED.js";
|
|
37
38
|
import {
|
|
38
39
|
errorHandlerMiddleware
|
|
39
|
-
} from "../chunk-
|
|
40
|
+
} from "../chunk-BKJPYZYO.js";
|
|
40
41
|
import "../chunk-XHZ5L4FO.js";
|
|
41
42
|
import "../chunk-2KDLKHTO.js";
|
|
43
|
+
import "../chunk-KXXROUA5.js";
|
|
42
44
|
import "../chunk-Y2JYA2AR.js";
|
|
43
45
|
import "../chunk-HCF3C3AQ.js";
|
|
44
46
|
import "../chunk-IYXMGIEP.js";
|
|
45
|
-
import "../chunk-KXXROUA5.js";
|
|
46
47
|
import "../chunk-SNVGJPPX.js";
|
|
47
48
|
import "../chunk-GFWGLWTS.js";
|
|
48
49
|
import "../chunk-OZE5KUS3.js";
|
|
49
|
-
import "../chunk-
|
|
50
|
+
import "../chunk-3GAY2T2A.js";
|
|
51
|
+
import "../chunk-AJLUPYCQ.js";
|
|
50
52
|
import "../chunk-AIH3F7JV.js";
|
|
51
53
|
import "../chunk-GQVBWZLH.js";
|
|
52
54
|
import "../chunk-OHBAELPZ.js";
|
|
@@ -68,6 +70,7 @@ export {
|
|
|
68
70
|
responseLoggerMiddleware,
|
|
69
71
|
securityMiddleware,
|
|
70
72
|
setAccessTokenBlacklistChecker,
|
|
73
|
+
setAllowedOrigins,
|
|
71
74
|
setRateLimitAdapter,
|
|
72
75
|
validateCorsConfiguration,
|
|
73
76
|
validateSecurityConfiguration,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
securityMiddleware,
|
|
3
|
+
setAllowedOrigins,
|
|
3
4
|
validateSecurityConfiguration
|
|
4
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-WMQE2YK7.js";
|
|
5
6
|
import "../chunk-GQVBWZLH.js";
|
|
6
7
|
import "../chunk-OHBAELPZ.js";
|
|
7
8
|
import "../chunk-SFPMFVSD.js";
|
|
@@ -10,5 +11,6 @@ import "../chunk-2QH66EVQ.js";
|
|
|
10
11
|
import "../chunk-ORMEWXMH.js";
|
|
11
12
|
export {
|
|
12
13
|
securityMiddleware,
|
|
14
|
+
setAllowedOrigins,
|
|
13
15
|
validateSecurityConfiguration
|
|
14
16
|
};
|
|
@@ -4,25 +4,26 @@ import {
|
|
|
4
4
|
withCustomApi,
|
|
5
5
|
withOptionalAuthApi,
|
|
6
6
|
withPublicApi
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-JJ3I57N2.js";
|
|
8
8
|
import "../chunk-62Q7DN5G.js";
|
|
9
9
|
import "../chunk-FPESPW6P.js";
|
|
10
10
|
import "../chunk-6MI4XLWC.js";
|
|
11
11
|
import "../chunk-OIRAH57Y.js";
|
|
12
|
-
import "../chunk-
|
|
13
|
-
import "../chunk-
|
|
12
|
+
import "../chunk-WMQE2YK7.js";
|
|
13
|
+
import "../chunk-HUDWNKGY.js";
|
|
14
14
|
import "../chunk-KGHWGLED.js";
|
|
15
|
-
import "../chunk-
|
|
15
|
+
import "../chunk-BKJPYZYO.js";
|
|
16
16
|
import "../chunk-XHZ5L4FO.js";
|
|
17
17
|
import "../chunk-2KDLKHTO.js";
|
|
18
|
+
import "../chunk-KXXROUA5.js";
|
|
18
19
|
import "../chunk-Y2JYA2AR.js";
|
|
19
20
|
import "../chunk-HCF3C3AQ.js";
|
|
20
21
|
import "../chunk-IYXMGIEP.js";
|
|
21
|
-
import "../chunk-KXXROUA5.js";
|
|
22
22
|
import "../chunk-SNVGJPPX.js";
|
|
23
23
|
import "../chunk-GFWGLWTS.js";
|
|
24
24
|
import "../chunk-OZE5KUS3.js";
|
|
25
|
-
import "../chunk-
|
|
25
|
+
import "../chunk-3GAY2T2A.js";
|
|
26
|
+
import "../chunk-AJLUPYCQ.js";
|
|
26
27
|
import "../chunk-AIH3F7JV.js";
|
|
27
28
|
import "../chunk-GQVBWZLH.js";
|
|
28
29
|
import "../chunk-OHBAELPZ.js";
|
package/dist/utils/index.js
CHANGED
package/dist/utils/ip-utils.d.ts
CHANGED
|
@@ -4,12 +4,15 @@ export declare function normalizeIP(ip: string): string;
|
|
|
4
4
|
/**
|
|
5
5
|
* 클라이언트 IP 주소 추출 (서버에서만 사용)
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* 1. CF-Connecting-IP: Cloudflare가
|
|
9
|
-
* 2. True-Client-IP: Cloudflare Enterprise에서
|
|
10
|
-
* 3. X-
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* 신뢰 가능한 프록시 헤더만 사용:
|
|
8
|
+
* 1. CF-Connecting-IP: Cloudflare Proxy가 강제 설정 (스푸핑 불가)
|
|
9
|
+
* 2. True-Client-IP: Cloudflare Enterprise에서 설정
|
|
10
|
+
* 3. X-Forwarded-For: 마지막 IP = 직전 프록시가 추가한 실제 클라이언트 IP
|
|
11
|
+
*
|
|
12
|
+
* 보안 참고:
|
|
13
|
+
* - X-Real-IP, X-Client-IP 등 클라이언트가 임의 설정 가능한 헤더는 사용하지 않음
|
|
14
|
+
* - X-Forwarded-For의 첫 번째 IP는 클라이언트가 조작 가능하므로 마지막 IP를 사용
|
|
15
|
+
* - Cloudflare Proxy 환경에서는 CF-Connecting-IP가 항상 설정되므로 폴백에 도달하지 않음
|
|
13
16
|
*/
|
|
14
17
|
export declare function extractClientIp(headers: Headers): string | null;
|
|
15
18
|
export declare function isValidIP(ip: string): boolean;
|
package/dist/utils/ip-utils.js
CHANGED
|
@@ -54,17 +54,14 @@ function extractClientIp(headers) {
|
|
|
54
54
|
if (cf && isValidIP(cf.trim())) return cf.trim();
|
|
55
55
|
const trueClientIp = headers.get("true-client-ip");
|
|
56
56
|
if (trueClientIp && isValidIP(trueClientIp.trim())) return trueClientIp.trim();
|
|
57
|
-
const xri = headers.get("x-real-ip");
|
|
58
|
-
if (xri && isValidIP(xri.trim())) return xri.trim();
|
|
59
57
|
const xff = headers.get("x-forwarded-for");
|
|
60
58
|
if (xff) {
|
|
61
59
|
const ips = xff.split(",").map((ip) => ip.trim()).filter(Boolean);
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
const lastIp = ips[ips.length - 1];
|
|
61
|
+
if (lastIp && isValidIP(lastIp)) {
|
|
62
|
+
return lastIp;
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
|
-
const xc = headers.get("x-client-ip");
|
|
67
|
-
if (xc && isValidIP(xc.trim())) return xc.trim();
|
|
68
65
|
return null;
|
|
69
66
|
}
|
|
70
67
|
function isValidIP(ip) {
|
|
@@ -8,15 +8,16 @@
|
|
|
8
8
|
*/
|
|
9
9
|
/**
|
|
10
10
|
* XSS 방지를 위한 HTML 태그 제거
|
|
11
|
-
* - 모든 HTML 태그 제거
|
|
12
|
-
* -
|
|
11
|
+
* - 모든 HTML 태그 제거 (named + numeric 엔티티로 인코딩된 태그 포함)
|
|
12
|
+
* - 닫는 >가 없는 불완전 태그 제거 (HTML5 파서 우회 방지)
|
|
13
|
+
* - HTML 엔티티를 디코딩하여 원본 텍스트 반환
|
|
13
14
|
* - 공백 정리
|
|
14
15
|
*
|
|
15
16
|
* @param input - 정제할 문자열
|
|
16
|
-
* @returns
|
|
17
|
+
* @returns 태그가 제거된 순수 텍스트 (React 등 텍스트 컨텍스트에서 사용)
|
|
17
18
|
*
|
|
18
19
|
* @example
|
|
19
|
-
* sanitizeHtml('<script>alert("xss")</script>Hello') // 'Hello'
|
|
20
|
+
* sanitizeHtml('<script>alert("xss")</script>Hello') // 'alert("xss")Hello'
|
|
20
21
|
* sanitizeHtml('Hello <b>World</b>') // 'Hello World'
|
|
21
22
|
*/
|
|
22
23
|
export declare function sanitizeHtml(input: string): string;
|
package/dist/utils/sanitizer.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import "../chunk-ORMEWXMH.js";
|
|
2
2
|
|
|
3
3
|
// src/utils/short-code-generator.ts
|
|
4
|
+
import { randomBytes } from "crypto";
|
|
4
5
|
function generateShortCode(length = 8) {
|
|
5
6
|
if (!Number.isInteger(length) || length <= 0) {
|
|
6
7
|
throw new Error("length must be a positive integer");
|
|
7
8
|
}
|
|
8
9
|
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
10
|
+
const bytes = randomBytes(length);
|
|
9
11
|
let result = "";
|
|
10
12
|
for (let i = 0; i < length; i++) {
|
|
11
|
-
result += chars.charAt(
|
|
13
|
+
result += chars.charAt(bytes[i] % chars.length);
|
|
12
14
|
}
|
|
13
15
|
return result;
|
|
14
16
|
}
|
|
@@ -39,11 +39,21 @@ var SUPPORTED_SCHEMES = [
|
|
|
39
39
|
"maps:",
|
|
40
40
|
"geo:"
|
|
41
41
|
];
|
|
42
|
+
var DANGEROUS_SCHEMES = [
|
|
43
|
+
"javascript:",
|
|
44
|
+
"vbscript:",
|
|
45
|
+
"data:",
|
|
46
|
+
"file:",
|
|
47
|
+
"blob:"
|
|
48
|
+
];
|
|
42
49
|
var CUSTOM_SCHEME_PATTERN = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;
|
|
43
50
|
function hasValidScheme(url) {
|
|
44
51
|
try {
|
|
45
52
|
const urlObj = new URL(url);
|
|
46
|
-
const protocol = urlObj.protocol;
|
|
53
|
+
const protocol = urlObj.protocol.toLowerCase();
|
|
54
|
+
if (DANGEROUS_SCHEMES.includes(protocol)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
47
57
|
if (SUPPORTED_SCHEMES.includes(protocol)) {
|
|
48
58
|
return true;
|
|
49
59
|
}
|
|
@@ -69,6 +79,9 @@ function normalizeUrl(url) {
|
|
|
69
79
|
}
|
|
70
80
|
const scheme = extractScheme(normalized);
|
|
71
81
|
if (scheme) {
|
|
82
|
+
if (DANGEROUS_SCHEMES.includes(scheme.toLowerCase() + ":")) {
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
72
85
|
return normalized;
|
|
73
86
|
}
|
|
74
87
|
if (normalized.startsWith("//")) {
|
|
@@ -88,6 +101,14 @@ function validateUrl(url, options) {
|
|
|
88
101
|
try {
|
|
89
102
|
const urlObj = new URL(normalized);
|
|
90
103
|
const protocol = urlObj.protocol;
|
|
104
|
+
if (DANGEROUS_SCHEMES.includes(protocol.toLowerCase())) {
|
|
105
|
+
return {
|
|
106
|
+
isValid: false,
|
|
107
|
+
message: `\uBCF4\uC548\uC0C1 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uD504\uB85C\uD1A0\uCF5C\uC785\uB2C8\uB2E4: ${protocol}`,
|
|
108
|
+
messageKey: "unsupportedProtocol",
|
|
109
|
+
messageParams: { protocol }
|
|
110
|
+
};
|
|
111
|
+
}
|
|
91
112
|
const isSupported = SUPPORTED_SCHEMES.includes(protocol);
|
|
92
113
|
const isCustom = CUSTOM_SCHEME_PATTERN.test(normalized);
|
|
93
114
|
if (!isSupported && !isCustom) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DataTableBody
|
|
3
|
-
} from "./chunk-7IY3RQQL.js";
|
|
4
1
|
import {
|
|
5
2
|
DataTableBulkActions
|
|
6
3
|
} from "./chunk-MAATEX2R.js";
|
|
@@ -16,6 +13,9 @@ import {
|
|
|
16
13
|
import {
|
|
17
14
|
DEFAULT_LABELS
|
|
18
15
|
} from "./chunk-NY5QXT33.js";
|
|
16
|
+
import {
|
|
17
|
+
DataTableBody
|
|
18
|
+
} from "./chunk-7IY3RQQL.js";
|
|
19
19
|
import {
|
|
20
20
|
Alert,
|
|
21
21
|
AlertDescription
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppError
|
|
3
|
+
} from "./chunk-KXXROUA5.js";
|
|
1
4
|
import {
|
|
2
5
|
getErrorDisplayInfo,
|
|
3
6
|
getFriendlyMessage
|
|
4
7
|
} from "./chunk-S54PFAEU.js";
|
|
5
|
-
import {
|
|
6
|
-
AppError
|
|
7
|
-
} from "./chunk-KXXROUA5.js";
|
|
8
8
|
import {
|
|
9
9
|
getErrorCategory
|
|
10
10
|
} from "./chunk-SNVGJPPX.js";
|