mirror of
https://git.openapi.site/https://github.com/desirecore/registry.git
synced 2026-10-21 22:33:23 +08:00
33 lines
2.4 KiB
JavaScript
33 lines
2.4 KiB
JavaScript
/** 作者材料的跨字段检查;身份与运行事实由客户端owner核验,不执行安装说明。 */
|
||
export function validateApplicationReleaseContract(manifest) {
|
||
if (manifest.type !== 'web-bundle' && manifest.appRelease === undefined) return []
|
||
const errors = []
|
||
if (!['web-bundle', 'native-app', 'docker-app'].includes(manifest.type)) errors.push('appRelease: unsupported delivery type')
|
||
if (!manifest.appRelease?.requiredCapabilities?.includes('app-authoring-v1')) errors.push('appRelease: app-authoring-v1 capability handshake required')
|
||
if (!manifest.appRelease?.requiredCapabilities?.includes('release') ||
|
||
(manifest.type === 'web-bundle' && !manifest.appRelease.requiredCapabilities.includes('web-bundle'))) errors.push('appRelease: delivery capability handshake required')
|
||
const images = manifest.appRelease?.imageRefs
|
||
if (manifest.type === 'docker-app' && (!Array.isArray(images) || !images.length || images.some(image =>
|
||
typeof image !== 'string' || !/^[a-z0-9][a-z0-9._:/-]*@sha256:[a-f0-9]{64}$/.test(image) ||
|
||
Number(image.split('/')[0].split(':')[1] ?? 0) > 65535))) errors.push('appRelease: Docker images must be pinned by digest')
|
||
if (manifest.type !== 'docker-app' && images !== undefined) errors.push('appRelease: imageRefs belongs only to Docker release')
|
||
const source = manifest.source
|
||
if (!source || !['git', 'release'].includes(source.kind) ||
|
||
!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(source.ref ?? '') || !/^[a-f0-9]{64}$/.test(source.sha256 ?? '')) {
|
||
errors.push('source: authored release requires immutable commit and archive sha256')
|
||
}
|
||
try {
|
||
const url = new URL(source?.url)
|
||
if (url.protocol !== 'https:' || url.username || url.password || url.hash || url.search) errors.push('source: credential-free fixed HTTPS URL required')
|
||
} catch { errors.push('source: invalid URL') }
|
||
if (source?.kind === 'git' && !isPortablePath(source.path)) errors.push('source: git archive path must be canonical and relative')
|
||
if (!isPortablePath(manifest.appRelease?.definitionPath)) errors.push('appRelease: definitionPath must be canonical and relative')
|
||
return errors
|
||
}
|
||
|
||
function isPortablePath(value) {
|
||
return typeof value === 'string' && value.length > 0 && value.length <= 512 &&
|
||
!/[\\\x00-\x1f?#:]/.test(value) && !value.startsWith('/') &&
|
||
value.split('/').every(part => part.length > 0 && part !== '.' && part !== '..')
|
||
}
|