mirror of
https://github.com/itdoginfo/podkop.git
synced 2025-12-09 13:06:52 +03:00
refactor: migrate Proxy Configuration URL validation to modular
This commit is contained in:
@@ -78,8 +78,9 @@ function createConfigSection(section, map, network) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -91,92 +92,30 @@ function createConfigSection(section, map, network) {
|
|||||||
return _('No active configuration found. At least one non-commented line is required.');
|
return _('No active configuration found. At least one non-commented line is required.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!activeConfig.startsWith('vless://') && !activeConfig.startsWith('ss://')) {
|
|
||||||
return _('URL must start with vless:// or ss://');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeConfig.startsWith('ss://')) {
|
if (activeConfig.startsWith('ss://')) {
|
||||||
let encrypted_part;
|
const validation = main.validateShadowsocksUrl(activeConfig);
|
||||||
try {
|
|
||||||
let mainPart = activeConfig.includes('?') ? activeConfig.split('?')[0] : activeConfig.split('#')[0];
|
if (validation.valid) {
|
||||||
encrypted_part = mainPart.split('/')[2].split('@')[0];
|
return true;
|
||||||
try {
|
|
||||||
let decoded = atob(encrypted_part);
|
|
||||||
if (!decoded.includes(':')) {
|
|
||||||
if (!encrypted_part.includes(':') && !encrypted_part.includes('-')) {
|
|
||||||
return _('Invalid Shadowsocks URL format: missing method and password separator ":"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (!encrypted_part.includes(':') && !encrypted_part.includes('-')) {
|
|
||||||
return _('Invalid Shadowsocks URL format: missing method and password separator ":"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
return _('Invalid Shadowsocks URL format');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return _(validation.message)
|
||||||
let serverPart = activeConfig.split('@')[1];
|
|
||||||
if (!serverPart) return _('Invalid Shadowsocks URL: missing server address');
|
|
||||||
let [server, portAndRest] = serverPart.split(':');
|
|
||||||
if (!server) return _('Invalid Shadowsocks URL: missing server');
|
|
||||||
let port = portAndRest ? portAndRest.split(/[?#]/)[0] : null;
|
|
||||||
if (!port) return _('Invalid Shadowsocks URL: missing port');
|
|
||||||
let portNum = parseInt(port);
|
|
||||||
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
||||||
return _('Invalid port number. Must be between 1 and 65535');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
return _('Invalid Shadowsocks URL: missing or invalid server/port format');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeConfig.startsWith('vless://')) {
|
if (activeConfig.startsWith('vless://')) {
|
||||||
let uuid = activeConfig.split('/')[2].split('@')[0];
|
const validation = main.validateVlessUrl(activeConfig);
|
||||||
if (!uuid || uuid.length === 0) return _('Invalid VLESS URL: missing UUID');
|
|
||||||
|
|
||||||
try {
|
|
||||||
let serverPart = activeConfig.split('@')[1];
|
|
||||||
if (!serverPart) return _('Invalid VLESS URL: missing server address');
|
|
||||||
let [server, portAndRest] = serverPart.split(':');
|
|
||||||
if (!server) return _('Invalid VLESS URL: missing server');
|
|
||||||
let port = portAndRest ? portAndRest.split(/[/?#]/)[0] : null;
|
|
||||||
if (!port) return _('Invalid VLESS URL: missing port');
|
|
||||||
let portNum = parseInt(port);
|
|
||||||
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
||||||
return _('Invalid port number. Must be between 1 and 65535');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
return _('Invalid VLESS URL: missing or invalid server/port format');
|
|
||||||
}
|
|
||||||
|
|
||||||
let queryString = activeConfig.split('?')[1];
|
|
||||||
if (!queryString) return _('Invalid VLESS URL: missing query parameters');
|
|
||||||
|
|
||||||
let params = new URLSearchParams(queryString.split('#')[0]);
|
|
||||||
let type = params.get('type');
|
|
||||||
const validTypes = ['tcp', 'raw', 'udp', 'grpc', 'http', 'ws'];
|
|
||||||
if (!type || !validTypes.includes(type)) {
|
|
||||||
return _('Invalid VLESS URL: type must be one of tcp, raw, udp, grpc, http, ws');
|
|
||||||
}
|
|
||||||
|
|
||||||
let security = params.get('security');
|
|
||||||
const validSecurities = ['tls', 'reality', 'none'];
|
|
||||||
if (!security || !validSecurities.includes(security)) {
|
|
||||||
return _('Invalid VLESS URL: security must be one of tls, reality, none');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (security === 'reality') {
|
|
||||||
if (!params.get('pbk')) return _('Invalid VLESS URL: missing pbk parameter for reality security');
|
|
||||||
if (!params.get('fp')) return _('Invalid VLESS URL: missing fp parameter for reality security');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (validation.valid) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _(validation.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return _('URL must start with vless:// or ss://')
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Validation error:', e);
|
return `${_('Invalid URL format:')} ${e?.message}`;
|
||||||
return _('Invalid URL format: ') + e.message;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,160 @@ function bulkValidate(values, validate) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// src/validators/validateShadowsocksUrl.ts
|
||||||
|
function validateShadowsocksUrl(url) {
|
||||||
|
if (!url.startsWith("ss://")) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid Shadowsocks URL: must start with ss://"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const mainPart = url.includes("?") ? url.split("?")[0] : url.split("#")[0];
|
||||||
|
const encryptedPart = mainPart.split("/")[2]?.split("@")[0];
|
||||||
|
if (!encryptedPart) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid Shadowsocks URL: missing credentials"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const decoded = atob(encryptedPart);
|
||||||
|
if (!decoded.includes(":")) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid Shadowsocks URL: decoded credentials must contain method:password"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!encryptedPart.includes(":") && !encryptedPart.includes("-")) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: 'Invalid Shadowsocks URL: missing method and password separator ":"'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const serverPart = url.split("@")[1];
|
||||||
|
if (!serverPart) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid Shadowsocks URL: missing server address"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const [server, portAndRest] = serverPart.split(":");
|
||||||
|
if (!server) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid Shadowsocks URL: missing server"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const port = portAndRest ? portAndRest.split(/[?#]/)[0] : null;
|
||||||
|
if (!port) {
|
||||||
|
return { valid: false, message: "Invalid Shadowsocks URL: missing port" };
|
||||||
|
}
|
||||||
|
const portNum = parseInt(port, 10);
|
||||||
|
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid port number. Must be between 1 and 65535"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return { valid: false, message: "Invalid Shadowsocks URL: parsing failed" };
|
||||||
|
}
|
||||||
|
return { valid: true, message: "Valid" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/validators/validateVlessUrl.ts
|
||||||
|
function validateVlessUrl(url) {
|
||||||
|
if (!url.startsWith("vless://")) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid VLESS URL: must start with vless://"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const uuid = url.split("/")[2]?.split("@")[0];
|
||||||
|
if (!uuid) {
|
||||||
|
return { valid: false, message: "Invalid VLESS URL: missing UUID" };
|
||||||
|
}
|
||||||
|
const serverPart = url.split("@")[1];
|
||||||
|
if (!serverPart) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid VLESS URL: missing server address"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const [server, portAndRest] = serverPart.split(":");
|
||||||
|
if (!server) {
|
||||||
|
return { valid: false, message: "Invalid VLESS URL: missing server" };
|
||||||
|
}
|
||||||
|
const port = portAndRest ? portAndRest.split(/[/?#]/)[0] : null;
|
||||||
|
if (!port) {
|
||||||
|
return { valid: false, message: "Invalid VLESS URL: missing port" };
|
||||||
|
}
|
||||||
|
const portNum = parseInt(port, 10);
|
||||||
|
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid port number. Must be between 1 and 65535"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const queryString = url.split("?")[1];
|
||||||
|
if (!queryString) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid VLESS URL: missing query parameters"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const params = new URLSearchParams(queryString.split("#")[0]);
|
||||||
|
const type = params.get("type");
|
||||||
|
const validTypes = ["tcp", "raw", "udp", "grpc", "http", "ws"];
|
||||||
|
if (!type || !validTypes.includes(type)) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid VLESS URL: type must be one of tcp, raw, udp, grpc, http, ws"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const security = params.get("security");
|
||||||
|
const validSecurities = ["tls", "reality", "none"];
|
||||||
|
if (!security || !validSecurities.includes(security)) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid VLESS URL: security must be one of tls, reality, none"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (security === "reality") {
|
||||||
|
if (!params.get("pbk")) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid VLESS URL: missing pbk parameter for reality security"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!params.get("fp")) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: "Invalid VLESS URL: missing fp parameter for reality security"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return { valid: false, message: "Invalid VLESS URL: parsing failed" };
|
||||||
|
}
|
||||||
|
return { valid: true, message: "Valid" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/helpers/getBaseUrl.ts
|
||||||
|
function getBaseUrl() {
|
||||||
|
const { protocol, hostname } = window.location;
|
||||||
|
return `${protocol}//${hostname}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/helpers/parseValueList.ts
|
||||||
|
function parseValueList(value) {
|
||||||
|
return value.split(/\n/).map((line) => line.split("//")[0]).join(" ").split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
// src/constants.ts
|
// src/constants.ts
|
||||||
var STATUS_COLORS = {
|
var STATUS_COLORS = {
|
||||||
SUCCESS: "#4caf50",
|
SUCCESS: "#4caf50",
|
||||||
@@ -227,17 +381,6 @@ var COMMAND_SCHEDULING = {
|
|||||||
P10_PRIORITY: 1900
|
P10_PRIORITY: 1900
|
||||||
// Lowest priority
|
// Lowest priority
|
||||||
};
|
};
|
||||||
|
|
||||||
// src/helpers/getBaseUrl.ts
|
|
||||||
function getBaseUrl() {
|
|
||||||
const { protocol, hostname } = window.location;
|
|
||||||
return `${protocol}//${hostname}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// src/helpers/parseValueList.ts
|
|
||||||
function parseValueList(value) {
|
|
||||||
return value.split(/\n/).map((line) => line.split("//")[0]).join(" ").split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
|
|
||||||
}
|
|
||||||
return baseclass.extend({
|
return baseclass.extend({
|
||||||
ALLOWED_WITH_RUSSIA_INSIDE,
|
ALLOWED_WITH_RUSSIA_INSIDE,
|
||||||
BOOTSTRAP_DNS_SERVER_OPTIONS,
|
BOOTSTRAP_DNS_SERVER_OPTIONS,
|
||||||
@@ -263,6 +406,8 @@ return baseclass.extend({
|
|||||||
validateDomain,
|
validateDomain,
|
||||||
validateIPV4,
|
validateIPV4,
|
||||||
validatePath,
|
validatePath,
|
||||||
|
validateShadowsocksUrl,
|
||||||
validateSubnet,
|
validateSubnet,
|
||||||
validateUrl
|
validateUrl,
|
||||||
|
validateVlessUrl
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user