Fix Duplicate/Edit cloning (volumes/settings) and initial image update check.
Release rolling / release (push) Successful in 9m49s
Release rolling / release (push) Successful in 9m49s
This commit is contained in:
+59
-12
@@ -94,23 +94,70 @@ function isValidPortMapping(portMapping) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates volume mount format
|
||||
* @param {string} volume - Volume mount string (e.g., "/host:/container:ro")
|
||||
* Mount option suffix (Docker bind mode flags).
|
||||
* @param {string} part
|
||||
*/
|
||||
function isMountModePart(part) {
|
||||
if (!part || typeof part !== 'string') return false;
|
||||
// e.g. ro, rw, ro,Z, rw,z, shared, rprivate, …
|
||||
return /^(?:ro|rw|z|Z|shared|rshared|slave|rslave|private|rprivate)(?:,(?:ro|rw|z|Z|shared|rshared|slave|rslave|private|rprivate))*$/i.test(
|
||||
part.trim()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates volume mount format.
|
||||
* Accepts bind mounts and named volumes:
|
||||
* /host/path:/container/path
|
||||
* /host/path:/container/path:ro
|
||||
* volume-name:/container/path
|
||||
* volume-name:/container/path:rw
|
||||
* /host:/container:ro,Z
|
||||
* @param {string} volume - Volume mount string
|
||||
* @returns {boolean} - True if valid
|
||||
*/
|
||||
function isValidVolumeMount(volume) {
|
||||
if (!volume || typeof volume !== 'string') return false;
|
||||
if (!volume.includes(':')) return false;
|
||||
|
||||
const parts = volume.split(':');
|
||||
if (parts.length < 2 || parts.length > 3) return false;
|
||||
|
||||
// Check for path traversal attempts
|
||||
if (parts.some(part => part.includes('..'))) return false;
|
||||
|
||||
// Basic path validation
|
||||
const pathPattern = /^(\/[^\/]+)*\/?$/;
|
||||
return parts.slice(0, 2).every(part => pathPattern.test(part) || part.startsWith('/'));
|
||||
// Block path traversal in any segment
|
||||
if (volume.includes('..')) return false;
|
||||
|
||||
const raw = volume.trim();
|
||||
const parts = raw.split(':');
|
||||
if (parts.length < 2) return false;
|
||||
|
||||
let source;
|
||||
let dest;
|
||||
if (parts.length >= 3 && isMountModePart(parts[parts.length - 1])) {
|
||||
dest = parts[parts.length - 2];
|
||||
source = parts.slice(0, -2).join(':');
|
||||
} else if (parts.length === 2) {
|
||||
source = parts[0];
|
||||
dest = parts[1];
|
||||
} else if (parts.length > 2) {
|
||||
// e.g. Windows drive or unusual source with colons: join all but last as source
|
||||
dest = parts[parts.length - 1];
|
||||
source = parts.slice(0, -1).join(':');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!source || !dest) return false;
|
||||
|
||||
// Container path must be absolute
|
||||
if (!dest.startsWith('/')) return false;
|
||||
|
||||
// Named volume (Docker volume name) OR host path (absolute/relative/~ / Windows drive)
|
||||
const namedVol = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/;
|
||||
const isNamed = namedVol.test(source);
|
||||
const isHostPath =
|
||||
source.startsWith('/') ||
|
||||
source.startsWith('./') ||
|
||||
source.startsWith('~/') ||
|
||||
source === '~' ||
|
||||
/^[A-Za-z]:[\\/]/.test(source);
|
||||
|
||||
return isNamed || isHostPath;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user