- Add optional Holepunch clone lag gate (holepunch-freshness-gate.json, verify-holepunch-clone-freshness.mjs) and wire into pretest/docs. - Extend stock ctx.bareOsHrpcRequest with disk.os replication routes; bump hrpc_allowlist_sketch proc to schema 2 with stockRoutes list. - Security posture: blindRelayAudit; hyper_multisig_trust_pointer schema 2 + vault multisig continuity env; login/unlock audit hook. - Syscalls schema 9 alignment (JSON schema, compatibility matrix, conformance matrix clock_gettime); boot budget telemetry schema 2 in metrics_live. - Coreutils hostname -s/--short man/options; rebuild kernel bins/man. - POSIX + P2P dashboard section in docs/README; handbook/DOCUMENTATION/ release-checklist/OTA/KERNEL_CONTRACT/PEAR-RUN and related reference updates. - verify-boot-policy-extension-signer-pins: scan kernel init fragments. Note: vendor drift section removed from kernel/lib/bare/README.md (intentional).
1263 lines
41 KiB
JavaScript
1263 lines
41 KiB
JavaScript
/**
|
|
* Concatenated into /boot/init.js before init-main.js by scripts/bundle-kernel-init.mjs.
|
|
* Boot policy merge: semver helpers, skip merge, kernel.d env guards, applyBootPolicyFile.
|
|
* @see kernel/lib/init/STRUCTURE.md
|
|
*/
|
|
|
|
/**
|
|
* Optional `/etc/bare-os/boot.policy.json` when `BARE_OS_BOOT_POLICY=1`:
|
|
* `{ "skipBootStages": ["rc.d"], "denyBootStages": ["onboot"], "minKernelCapabilitiesPrimary": 1, "requireSeedCaps": 1,
|
|
* "maxExecLineDepth": 32, "denyEnvKeys": ["FOO"], "requireProcNodes": ["/proc/bare_os/features"] }`.
|
|
* Boot policy v5 (optional): `requireKernelCapabilitiesHostTransportDelegates`, `requireInitJsSha256` (hex sha256 of `/boot/init.js` raw bytes).
|
|
* Boot policy v6 (optional): `requireKernelCapabilitiesReplicationOperatorSurface`, `requireBooterSemver`, `requireCtxApiMin`, `denyKernelExtensionIds`,
|
|
* `kernelExtensionHashPins`, `offlineLkgIntegrityStrict`.
|
|
* Boot policy v7 (optional): `requireKernelCapabilitiesPearCorestoreHrpc`, `requirePearRuntimeMin`, `requireProtocolPackageMin`,
|
|
* `denyCtxMethodPrefixes`, `maxKernelExtensionDepth`, `gitPartialClonePolicy`.
|
|
* Boot policy v8 (optional): `requireKernelCapabilitiesBareRuntimeProtoMux`, `requireBareRuntimeMin`, `denySeedRpcMethods`,
|
|
* `maxProtomuxChannelNameLength`.
|
|
* Boot policy v9 (optional): `requireKernelCapabilitiesBareModuleCryptoStaging`, `requirePearRuntimeRange` (`{ min?, max? }` semver),
|
|
* `denyBareModuleSpecifierPatterns`, `requireBareCryptoMin`, `denyKernelSyscalls`, `requirePearIpcMin`,
|
|
* `extensionSignerPinsV2`, `offlineLkgManifestMaxAgeSec`, `bootStagesRequireProcIndexMinSchema` (legacy `bootPhasesRequireProcIndexMinSchema`).
|
|
* Boot policy v10 (optional): `requireKernelCapabilitiesPearInspectLoggerTls`, `requireBareBootMin`, `denyBareRpcMethodPatterns`,
|
|
* `maxPearInspectDepth`, `requireBareLoggerMin`, `denyAutobaseDiscoveryChannels`, `requireBareTlsMin`,
|
|
* `extensionSignerPinsV3`, `offlineLkgRequirePearStamp`, `bootStagesRequireLifecycleMinSchema` (legacy `bootPhasesRequireLifecycleMinSchema`).
|
|
* Boot policy v11 (optional): `requireKernelCapabilitiesHypercorePackHrpcLifecycle`, `extensionSignerPinsV4`, `requireBarePackMin`,
|
|
* `requireBareAddonPolicyMin`, `maxHrpcAllowlistDepth`, `offlineLkgRequireHypercorePackHrpcLifecycle`.
|
|
* @param {Record<string, unknown>} ctx
|
|
* @returns {Promise<boolean>} false when strict policy fails (caller should abort boot)
|
|
*/
|
|
/**
|
|
* @param {string} s
|
|
* @returns {number[] | null}
|
|
*/
|
|
function parseSemverTriplet(s) {
|
|
const m = String(s)
|
|
.trim()
|
|
.match(/^(\d+)\.(\d+)\.(\d+)/)
|
|
if (!m) return null
|
|
return [Number(m[1]), Number(m[2]), Number(m[3])]
|
|
}
|
|
|
|
/**
|
|
* @param {string} have
|
|
* @param {string} need
|
|
*/
|
|
function semverGte(have, need) {
|
|
const a = parseSemverTriplet(have)
|
|
const b = parseSemverTriplet(need)
|
|
if (!a || !b) return false
|
|
for (let i = 0; i < 3; i++) {
|
|
if (a[i] > b[i]) return true
|
|
if (a[i] < b[i]) return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* @param {string} have
|
|
* @param {string} need
|
|
*/
|
|
function semverLte(have, need) {
|
|
const a = parseSemverTriplet(have)
|
|
const b = parseSemverTriplet(need)
|
|
if (!a || !b) return false
|
|
for (let i = 0; i < 3; i++) {
|
|
if (a[i] < b[i]) return true
|
|
if (a[i] > b[i]) return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* @param {unknown} m
|
|
* @param {string} k
|
|
* @returns {number | undefined}
|
|
*/
|
|
function ctxKernelCapWord(m, k) {
|
|
if (!m || typeof m !== 'object') return undefined
|
|
const v = /** @type {Record<string, unknown>} */ (m)[k]
|
|
return typeof v === 'number' ? v : undefined
|
|
}
|
|
|
|
/**
|
|
* Merge only boot-stage skip fields from a policy fragment (tiered fallbacks / rollback marker).
|
|
* Honors canonical `skipBootStages` / `denyBootStages` and legacy `skipPhases` / `denyBootPhases`.
|
|
* @param {Set<string>} merge
|
|
* @param {Record<string, unknown>} pol
|
|
*/
|
|
function mergeBootPolicySkipStagesIntoSet(merge, pol) {
|
|
if (!pol || typeof pol !== 'object') return
|
|
if (Array.isArray(pol.skipBootStages)) {
|
|
for (const p of pol.skipBootStages) merge.add(String(p).toLowerCase())
|
|
}
|
|
if (Array.isArray(pol.skipPhases)) {
|
|
for (const p of pol.skipPhases) merge.add(String(p).toLowerCase())
|
|
}
|
|
if (Array.isArray(pol.denyBootStages)) {
|
|
for (const p of pol.denyBootStages) merge.add(String(p).toLowerCase())
|
|
}
|
|
if (Array.isArray(pol.denyBootPhases)) {
|
|
for (const p of pol.denyBootPhases) merge.add(String(p).toLowerCase())
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Optional dry-run — parse boot snippets but skip trusted execLine / extension scripts.
|
|
* @param {Record<string, unknown>} ctx
|
|
*/
|
|
function bootDryRun(ctx) {
|
|
const v = ctx.env && ctx.env.BARE_OS_BOOT_DRY_RUN
|
|
return v === '1' || v === 'true'
|
|
}
|
|
|
|
/**
|
|
* Named boot stage for journals / checkpoints.
|
|
* @param {string} label
|
|
*/
|
|
function bootStageForPhase(label) {
|
|
const l = String(label).toLowerCase()
|
|
if (l === 'boot.policy') return 'policy'
|
|
if (l === 'os-release' || l === 'motd') return 'preflight'
|
|
if (
|
|
l === 'rc' ||
|
|
l.startsWith('rc.') ||
|
|
l === 'rc.d' ||
|
|
l === 'rc.local'
|
|
)
|
|
return 'rc'
|
|
if (l === 'kernel.d' || l === 'kernel.ext.d') return 'extensions'
|
|
if (l === 'onboot' || l === 'selftest') return 'services'
|
|
if (l === 'banner') return 'shell'
|
|
return 'misc'
|
|
}
|
|
|
|
/**
|
|
* kernel.d: leading `# ConditionEnvironment=KEY=VAL` / `# AssertEnvironment=…` guards.
|
|
* @param {Record<string, unknown>} ctx
|
|
* @param {string} text
|
|
*/
|
|
function kernelSnippetEnvGuardsOk(ctx, text) {
|
|
const env = ctx.env && typeof ctx.env === 'object' ? ctx.env : {}
|
|
for (const raw of String(text).split(/\r?\n/)) {
|
|
const line = raw.trim()
|
|
if (!line.startsWith('#')) break
|
|
const m = line.match(
|
|
/^#\s*ConditionEnvironment=(.+)$/i
|
|
) || line.match(/^#\s*AssertEnvironment=(.+)$/i)
|
|
if (!m) continue
|
|
const expr = String(m[1]).trim()
|
|
const eq = expr.indexOf('=')
|
|
if (eq < 1) return false
|
|
const key = expr.slice(0, eq).trim()
|
|
const need = expr.slice(eq + 1).trim()
|
|
const have =
|
|
env[key] != null && env[key] !== undefined ? String(env[key]) : ''
|
|
if (have !== need) return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
async function applyBootPolicyFile(ctx) {
|
|
const en = ctx.env && ctx.env.BARE_OS_BOOT_POLICY
|
|
if (en !== '1' && en !== 'true') return true
|
|
const merge =
|
|
ctx.bareOsBootPolicySkipStages instanceof Set
|
|
? ctx.bareOsBootPolicySkipStages
|
|
: ctx.bareOsBootPolicySkipPhases
|
|
if (!(merge instanceof Set)) return true
|
|
const { drive, b4a } = ctx
|
|
const strictPol =
|
|
ctx.env?.BARE_OS_BOOT_POLICY_STRICT === '1' ||
|
|
ctx.env?.BARE_OS_BOOT_POLICY_STRICT === 'true'
|
|
const advWords = ctx.bareOsAdvertisedKernelCapabilityWords
|
|
try {
|
|
const rb = ctx.env?.BARE_OS_BOOT_ROLLBACK_APPLY
|
|
if ((rb === '1' || rb === 'true') && ctx.vfs && typeof ctx.vfs.readFile === 'function') {
|
|
try {
|
|
const mbuf = await ctx.vfs.readFile('/run/bare-os/boot-rollback.marker')
|
|
const mobj = JSON.parse(b4a.toString(mbuf))
|
|
mergeBootPolicySkipStagesIntoSet(merge, mobj)
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
const policyPathRaw = String(ctx.env?.BARE_OS_BOOT_POLICY_PATH || '').trim()
|
|
const policyPath =
|
|
policyPathRaw && policyPathRaw.startsWith('/etc/bare-os/')
|
|
? policyPathRaw
|
|
: '/etc/bare-os/boot.policy.json'
|
|
const buf = await drive.get(policyPath)
|
|
if (!buf) return true
|
|
const pol = JSON.parse(b4a.toString(buf))
|
|
if (!pol || typeof pol !== 'object') return true
|
|
const legacySkip =
|
|
(Array.isArray(pol.skipPhases) && pol.skipPhases.length > 0) ||
|
|
(Array.isArray(pol.denyBootPhases) && pol.denyBootPhases.length > 0)
|
|
const modernSkip =
|
|
(Array.isArray(pol.skipBootStages) && pol.skipBootStages.length > 0) ||
|
|
(Array.isArray(pol.denyBootStages) && pol.denyBootStages.length > 0)
|
|
if (legacySkip && !modernSkip) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'warn',
|
|
'deprecatedSkipPhases',
|
|
'[boot-policy] deprecated: skipPhases/denyBootPhases in boot.policy.json — prefer skipBootStages/denyBootStages'
|
|
)
|
|
}
|
|
mergeBootPolicySkipStagesIntoSet(merge, pol)
|
|
if (Array.isArray(pol.policyFallbackPaths)) {
|
|
for (const rel of pol.policyFallbackPaths) {
|
|
const p2 = String(rel).trim()
|
|
if (!p2.startsWith('/etc/bare-os/')) continue
|
|
try {
|
|
const fb = await drive.get(p2)
|
|
if (!fb) continue
|
|
const pol2 = JSON.parse(b4a.toString(fb))
|
|
mergeBootPolicySkipStagesIntoSet(merge, pol2)
|
|
} catch (e) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'policyFallbackPaths',
|
|
'[boot-policy] policyFallbackPaths ' +
|
|
p2 +
|
|
': ' +
|
|
((e && e.message) || String(e))
|
|
)
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireBootBundleSha256Hex === 'string' &&
|
|
String(pol.requireBootBundleSha256Hex).trim()
|
|
) {
|
|
const need = String(pol.requireBootBundleSha256Hex).trim().toLowerCase()
|
|
const have = String(ctx.env?.BARE_OS_BOOT_BUNDLE_DIGEST_HEX || '')
|
|
.trim()
|
|
.toLowerCase()
|
|
if (!have || have !== need) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireBootBundleSha256Hex',
|
|
'[boot-policy] requireBootBundleSha256Hex not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (pol.initdAdmission && typeof pol.initdAdmission === 'object' && ctx.env) {
|
|
const a = /** @type {Record<string, unknown>} */ (pol.initdAdmission)
|
|
if (typeof a.maxParallel === 'number' && a.maxParallel >= 1) {
|
|
ctx.env.BARE_OS_INITD_MAX_PARALLEL = String(
|
|
Math.min(32, Math.floor(a.maxParallel))
|
|
)
|
|
}
|
|
if (typeof a.memoryHintMb === 'number' && a.memoryHintMb >= 1) {
|
|
ctx.env.BARE_OS_INITD_MEMORY_HINT_MB = String(
|
|
Math.min(1e6, Math.floor(a.memoryHintMb))
|
|
)
|
|
}
|
|
if (typeof a.wallBudgetMsDefault === 'number' && a.wallBudgetMsDefault >= 1) {
|
|
ctx.env.BARE_OS_INITD_WALL_BUDGET_MS_DEFAULT = String(
|
|
Math.min(3.6e6, Math.floor(a.wallBudgetMsDefault))
|
|
)
|
|
}
|
|
}
|
|
if (pol.p2pAdmission && typeof pol.p2pAdmission === 'object' && ctx.env) {
|
|
const p2p = /** @type {Record<string, unknown>} */ (pol.p2pAdmission)
|
|
const allow = typeof p2p.peerAllowlistHex === 'string' ? p2p.peerAllowlistHex.trim() : ''
|
|
if (allow && !String(ctx.env.BARE_OS_PEER_ALLOWLIST_HEX || '').trim()) {
|
|
ctx.env.BARE_OS_PEER_ALLOWLIST_HEX = allow
|
|
}
|
|
const boot =
|
|
typeof p2p.hyperswarmBootstrap === 'string' ? p2p.hyperswarmBootstrap.trim() : ''
|
|
if (boot && !String(ctx.env.HYPERSWARM_BOOTSTRAP || '').trim()) {
|
|
ctx.env.HYPERSWARM_BOOTSTRAP = boot
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.minKernelCapabilitiesPrimary === 'number' &&
|
|
pol.minKernelCapabilitiesPrimary > 0
|
|
) {
|
|
const adv = ctxKernelCapWord(advWords, 'primary')
|
|
const need = pol.minKernelCapabilitiesPrimary >>> 0
|
|
if (typeof adv === 'number') {
|
|
const ok = ((adv >>> 0) & need) === need
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'minKernelCapabilitiesPrimary',
|
|
'[boot-policy] minKernelCapabilitiesPrimary not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (typeof pol.requireSeedCaps === 'number' && pol.requireSeedCaps > 0) {
|
|
const seed = ctxKernelCapWord(ctx.bareOsSeedKernelCapabilityWords, 'primary')
|
|
const need = pol.requireSeedCaps >>> 0
|
|
const ok = typeof seed === 'number' && ((seed >>> 0) & need) === need
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireSeedCaps',
|
|
'[boot-policy] requireSeedCaps not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireKernelCapabilitiesExtendedSeedingPlatform === 'number' &&
|
|
pol.requireKernelCapabilitiesExtendedSeedingPlatform > 0
|
|
) {
|
|
const adv2 = ctxKernelCapWord(advWords, 'extendedSeedingPlatform')
|
|
const need2 = pol.requireKernelCapabilitiesExtendedSeedingPlatform >>> 0
|
|
const ok = typeof adv2 === 'number' && ((adv2 >>> 0) & need2) === need2
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireKernelCapabilitiesExtendedSeedingPlatform',
|
|
'[boot-policy] requireKernelCapabilitiesExtendedSeedingPlatform not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireKernelCapabilitiesRlimitsDelegatesShell === 'number' &&
|
|
pol.requireKernelCapabilitiesRlimitsDelegatesShell > 0
|
|
) {
|
|
const adv3 = ctxKernelCapWord(advWords, 'rlimitsDelegatesShell')
|
|
const need3 = pol.requireKernelCapabilitiesRlimitsDelegatesShell >>> 0
|
|
const ok = typeof adv3 === 'number' && ((adv3 >>> 0) & need3) === need3
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireKernelCapabilitiesRlimitsDelegatesShell',
|
|
'[boot-policy] requireKernelCapabilitiesRlimitsDelegatesShell not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireKernelCapabilitiesOfflineNetExtensions === 'number' &&
|
|
pol.requireKernelCapabilitiesOfflineNetExtensions > 0
|
|
) {
|
|
const adv4 = ctxKernelCapWord(advWords, 'offlineNetExtensions')
|
|
const need4 = pol.requireKernelCapabilitiesOfflineNetExtensions >>> 0
|
|
const ok = typeof adv4 === 'number' && ((adv4 >>> 0) & need4) === need4
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireKernelCapabilitiesOfflineNetExtensions',
|
|
'[boot-policy] requireKernelCapabilitiesOfflineNetExtensions not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireKernelCapabilitiesHostTransportDelegates === 'number' &&
|
|
pol.requireKernelCapabilitiesHostTransportDelegates > 0
|
|
) {
|
|
const adv5 = ctxKernelCapWord(advWords, 'hostTransportDelegates')
|
|
const need5 = pol.requireKernelCapabilitiesHostTransportDelegates >>> 0
|
|
const ok = typeof adv5 === 'number' && ((adv5 >>> 0) & need5) === need5
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireKernelCapabilitiesHostTransportDelegates',
|
|
'[boot-policy] requireKernelCapabilitiesHostTransportDelegates not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireKernelCapabilitiesReplicationOperatorSurface === 'number' &&
|
|
pol.requireKernelCapabilitiesReplicationOperatorSurface > 0
|
|
) {
|
|
const adv6 = ctxKernelCapWord(advWords, 'replicationOperatorSurface')
|
|
const need6 = pol.requireKernelCapabilitiesReplicationOperatorSurface >>> 0
|
|
const ok = typeof adv6 === 'number' && ((adv6 >>> 0) & need6) === need6
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireKernelCapabilitiesReplicationOperatorSurface',
|
|
'[boot-policy] requireKernelCapabilitiesReplicationOperatorSurface not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireKernelCapabilitiesPearCorestoreHrpc === 'number' &&
|
|
pol.requireKernelCapabilitiesPearCorestoreHrpc > 0
|
|
) {
|
|
const adv7 = ctxKernelCapWord(advWords, 'pearCorestoreHrpc')
|
|
const need7 = pol.requireKernelCapabilitiesPearCorestoreHrpc >>> 0
|
|
const ok = typeof adv7 === 'number' && ((adv7 >>> 0) & need7) === need7
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireKernelCapabilitiesPearCorestoreHrpc',
|
|
'[boot-policy] requireKernelCapabilitiesPearCorestoreHrpc not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireKernelCapabilitiesBareRuntimeProtoMux === 'number' &&
|
|
pol.requireKernelCapabilitiesBareRuntimeProtoMux > 0
|
|
) {
|
|
const adv8 = ctxKernelCapWord(advWords, 'bareRuntimeProtoMux')
|
|
const need8 = pol.requireKernelCapabilitiesBareRuntimeProtoMux >>> 0
|
|
const ok = typeof adv8 === 'number' && ((adv8 >>> 0) & need8) === need8
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireKernelCapabilitiesBareRuntimeProtoMux',
|
|
'[boot-policy] requireKernelCapabilitiesBareRuntimeProtoMux not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireKernelCapabilitiesBareModuleCryptoStaging === 'number' &&
|
|
pol.requireKernelCapabilitiesBareModuleCryptoStaging > 0
|
|
) {
|
|
const adv9 = ctxKernelCapWord(advWords, 'bareModuleCryptoStaging')
|
|
const need9 = pol.requireKernelCapabilitiesBareModuleCryptoStaging >>> 0
|
|
const ok = typeof adv9 === 'number' && ((adv9 >>> 0) & need9) === need9
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireKernelCapabilitiesBareModuleCryptoStaging',
|
|
'[boot-policy] requireKernelCapabilitiesBareModuleCryptoStaging not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireKernelCapabilitiesPearInspectLoggerTls === 'number' &&
|
|
pol.requireKernelCapabilitiesPearInspectLoggerTls > 0
|
|
) {
|
|
const adv10 = ctxKernelCapWord(advWords, 'pearInspectLoggerTls')
|
|
const need10 = pol.requireKernelCapabilitiesPearInspectLoggerTls >>> 0
|
|
const ok =
|
|
typeof adv10 === 'number' && ((adv10 >>> 0) & need10) === need10
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireKernelCapabilitiesPearInspectLoggerTls',
|
|
'[boot-policy] requireKernelCapabilitiesPearInspectLoggerTls not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireKernelCapabilitiesHypercorePackHrpcLifecycle === 'number' &&
|
|
pol.requireKernelCapabilitiesHypercorePackHrpcLifecycle > 0
|
|
) {
|
|
const adv11 = ctxKernelCapWord(advWords, 'hypercorePackHrpcLifecycle')
|
|
const need11 = pol.requireKernelCapabilitiesHypercorePackHrpcLifecycle >>> 0
|
|
const ok =
|
|
typeof adv11 === 'number' && ((adv11 >>> 0) & need11) === need11
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireKernelCapabilitiesHypercorePackHrpcLifecycle',
|
|
'[boot-policy] requireKernelCapabilitiesHypercorePackHrpcLifecycle not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
pol.requirePearRuntimeRange &&
|
|
typeof pol.requirePearRuntimeRange === 'object'
|
|
) {
|
|
const r = pol.requirePearRuntimeRange
|
|
const minS = typeof r.min === 'string' ? r.min.trim() : ''
|
|
const maxS = typeof r.max === 'string' ? r.max.trim() : ''
|
|
const haveS =
|
|
typeof ctx.bareOsPearRuntimeVersion === 'string'
|
|
? ctx.bareOsPearRuntimeVersion.trim()
|
|
: ''
|
|
let ok = Boolean(haveS)
|
|
if (ok && minS) ok = semverGte(haveS, minS)
|
|
if (ok && maxS) ok = semverLte(haveS, maxS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requirePearRuntimeRange',
|
|
'[boot-policy] requirePearRuntimeRange not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireBareRuntimeMin === 'string' &&
|
|
String(pol.requireBareRuntimeMin).trim()
|
|
) {
|
|
const needS = String(pol.requireBareRuntimeMin).trim()
|
|
const haveS =
|
|
typeof ctx.bareOsBareRuntimeVersion === 'string'
|
|
? ctx.bareOsBareRuntimeVersion.trim()
|
|
: ''
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireBareRuntimeMin',
|
|
'[boot-policy] requireBareRuntimeMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requirePearRuntimeMin === 'string' &&
|
|
String(pol.requirePearRuntimeMin).trim()
|
|
) {
|
|
const needS = String(pol.requirePearRuntimeMin).trim()
|
|
const haveS =
|
|
typeof ctx.bareOsPearRuntimeVersion === 'string'
|
|
? ctx.bareOsPearRuntimeVersion.trim()
|
|
: ''
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requirePearRuntimeMin',
|
|
'[boot-policy] requirePearRuntimeMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireProtocolPackageMin === 'string' &&
|
|
String(pol.requireProtocolPackageMin).trim()
|
|
) {
|
|
const needS = String(pol.requireProtocolPackageMin).trim()
|
|
const haveS =
|
|
typeof ctx.bareOsProtocolPackageVersion === 'string'
|
|
? ctx.bareOsProtocolPackageVersion.trim()
|
|
: ''
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireProtocolPackageMin',
|
|
'[boot-policy] requireProtocolPackageMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireBooterSemver === 'string' &&
|
|
String(pol.requireBooterSemver).trim()
|
|
) {
|
|
const needS = String(pol.requireBooterSemver).trim()
|
|
const haveS =
|
|
typeof ctx.bareOsBooterPackageVersion === 'string'
|
|
? ctx.bareOsBooterPackageVersion.trim()
|
|
: ''
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireBooterSemver',
|
|
'[boot-policy] requireBooterSemver not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireCtxApiMin === 'string' &&
|
|
String(pol.requireCtxApiMin).trim()
|
|
) {
|
|
const needS = String(pol.requireCtxApiMin).trim()
|
|
const haveS =
|
|
typeof ctx.bareOsCtxApiVersion === 'string'
|
|
? ctx.bareOsCtxApiVersion.trim()
|
|
: String(ctx.bareOsCtxApiVersion || '').trim()
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireCtxApiMin',
|
|
'[boot-policy] requireCtxApiMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (Array.isArray(pol.denyKernelExtensionIds) && ctx.env) {
|
|
const parts = pol.denyKernelExtensionIds
|
|
.map((x) => String(x).trim())
|
|
.filter(Boolean)
|
|
if (parts.length)
|
|
ctx.env.BARE_OS_BOOT_POLICY_DENY_KERNEL_EXT_IDS = parts.join(',')
|
|
}
|
|
if (
|
|
pol.kernelExtensionHashPins &&
|
|
typeof pol.kernelExtensionHashPins === 'object' &&
|
|
ctx.env
|
|
) {
|
|
try {
|
|
ctx.env.BARE_OS_BOOT_POLICY_EXTENSION_HASH_PINS_JSON = JSON.stringify(
|
|
pol.kernelExtensionHashPins
|
|
)
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
if (pol.offlineLkgIntegrityStrict === true && ctx.env) {
|
|
ctx.env.BARE_OS_OFFLINE_LKG_INTEGRITY_STRICT = '1'
|
|
}
|
|
if (
|
|
typeof pol.requireInitJsSha256 === 'string' &&
|
|
String(pol.requireInitJsSha256).trim()
|
|
) {
|
|
const need = String(pol.requireInitJsSha256).trim().toLowerCase()
|
|
const buf = await drive.get('/boot/init.js')
|
|
const hashFn = ctx.bareOsBootFileSha256Hex
|
|
if (typeof hashFn !== 'function') {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireInitJsSha256MissingHasher',
|
|
'[boot-policy] requireInitJsSha256 needs ctx.bareOsBootFileSha256Hex'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
} else {
|
|
const hex = hashFn(buf || new Uint8Array())
|
|
if (hex !== need) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireInitJsSha256Mismatch',
|
|
'[boot-policy] requireInitJsSha256 mismatch for /boot/init.js'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (Array.isArray(pol.allowedPearIpcChannels) && ctx.env) {
|
|
const parts = pol.allowedPearIpcChannels
|
|
.map((c) => String(c).trim())
|
|
.filter(Boolean)
|
|
if (parts.length)
|
|
ctx.env.BARE_OS_BOOT_POLICY_PEAR_IPC_CHANNELS = parts.join(',')
|
|
}
|
|
if (Array.isArray(pol.denyVfsPrefixes) && ctx.env) {
|
|
const parts = pol.denyVfsPrefixes
|
|
.map((p) => String(p).trim())
|
|
.filter(Boolean)
|
|
if (parts.length) ctx.env.BARE_OS_BOOT_POLICY_DENY_VFS = parts.join(',')
|
|
}
|
|
if (Array.isArray(pol.denyExecLineBuiltins) && ctx.env) {
|
|
const parts = pol.denyExecLineBuiltins
|
|
.map((b) => String(b).trim())
|
|
.filter(Boolean)
|
|
if (parts.length) {
|
|
ctx.env.BARE_OS_BOOT_POLICY_DENY_EXEC_LINE_BUILTINS =
|
|
parts.join(',')
|
|
}
|
|
}
|
|
if (Array.isArray(pol.allowedCtxMethods) && ctx.env) {
|
|
const parts = pol.allowedCtxMethods
|
|
.map((m) => String(m).trim())
|
|
.filter(Boolean)
|
|
if (parts.length) {
|
|
ctx.env.BARE_OS_BOOT_POLICY_ALLOWED_CTX_METHODS = parts.join(',')
|
|
}
|
|
}
|
|
if (Array.isArray(pol.denyCtxMethodPrefixes) && ctx.env) {
|
|
const parts = pol.denyCtxMethodPrefixes
|
|
.map((m) => String(m).trim())
|
|
.filter(Boolean)
|
|
if (parts.length) {
|
|
ctx.env.BARE_OS_BOOT_POLICY_DENY_CTX_PREFIXES = parts.join(',')
|
|
}
|
|
}
|
|
if (Array.isArray(pol.denySeedRpcMethods) && ctx.env) {
|
|
const parts = pol.denySeedRpcMethods
|
|
.map((m) => String(m).trim())
|
|
.filter(Boolean)
|
|
const existing = String(
|
|
ctx.env.BARE_OS_BOOT_POLICY_DENY_SEED_RPC_METHODS || ''
|
|
)
|
|
.split(',')
|
|
.map((s) => s.trim())
|
|
.filter(Boolean)
|
|
const merged = [...new Set([...existing, ...parts])]
|
|
if (merged.length) {
|
|
ctx.env.BARE_OS_BOOT_POLICY_DENY_SEED_RPC_METHODS = merged.join(',')
|
|
}
|
|
}
|
|
if (
|
|
Array.isArray(pol.denyBareModuleSpecifierPatterns) &&
|
|
ctx.env
|
|
) {
|
|
try {
|
|
ctx.env.BARE_OS_BOOT_POLICY_DENY_BARE_MODULE_SPECS_JSON =
|
|
JSON.stringify(pol.denyBareModuleSpecifierPatterns)
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
if (
|
|
Array.isArray(pol.denyKernelSyscalls) &&
|
|
ctx.env
|
|
) {
|
|
const parts = pol.denyKernelSyscalls
|
|
.map((s) => String(s).trim())
|
|
.filter(Boolean)
|
|
if (parts.length) {
|
|
ctx.env.BARE_OS_BOOT_POLICY_DENY_KERNEL_SYSCALLS = parts.join(',')
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireBareCryptoMin === 'string' &&
|
|
String(pol.requireBareCryptoMin).trim()
|
|
) {
|
|
const needS = String(pol.requireBareCryptoMin).trim()
|
|
const haveS =
|
|
ctx.env && String(ctx.env.BARE_OS_BARE_CRYPTO_VERSION || '').trim()
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireBareCryptoMin',
|
|
'[boot-policy] requireBareCryptoMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requirePearIpcMin === 'string' &&
|
|
String(pol.requirePearIpcMin).trim()
|
|
) {
|
|
const needS = String(pol.requirePearIpcMin).trim()
|
|
const haveS =
|
|
ctx.env && String(ctx.env.BARE_OS_PEAR_IPC_PACKAGE_VERSION || '').trim()
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requirePearIpcMin',
|
|
'[boot-policy] requirePearIpcMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
pol.extensionSignerPinsV2 &&
|
|
typeof pol.extensionSignerPinsV2 === 'object' &&
|
|
ctx.env
|
|
) {
|
|
try {
|
|
ctx.env.BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V2_JSON =
|
|
JSON.stringify(pol.extensionSignerPinsV2)
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
if (
|
|
pol.extensionSignerPinsV3 &&
|
|
typeof pol.extensionSignerPinsV3 === 'object' &&
|
|
ctx.env
|
|
) {
|
|
try {
|
|
ctx.env.BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V3_JSON =
|
|
JSON.stringify(pol.extensionSignerPinsV3)
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
if (
|
|
pol.extensionSignerPinsV4 &&
|
|
typeof pol.extensionSignerPinsV4 === 'object' &&
|
|
ctx.env
|
|
) {
|
|
try {
|
|
ctx.env.BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V4_JSON =
|
|
JSON.stringify(pol.extensionSignerPinsV4)
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
if (
|
|
pol.extensionSignerPinsV5 &&
|
|
typeof pol.extensionSignerPinsV5 === 'object' &&
|
|
ctx.env
|
|
) {
|
|
try {
|
|
ctx.env.BARE_OS_BOOT_POLICY_EXTENSION_SIGNER_PINS_V5_JSON =
|
|
JSON.stringify(pol.extensionSignerPinsV5)
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireBareBootMin === 'string' &&
|
|
String(pol.requireBareBootMin).trim()
|
|
) {
|
|
const needS = String(pol.requireBareBootMin).trim()
|
|
const haveS =
|
|
ctx.env && String(ctx.env.BARE_OS_BARE_BOOT_VERSION || '').trim()
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireBareBootMin',
|
|
'[boot-policy] requireBareBootMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
Array.isArray(pol.denyBareRpcMethodPatterns) &&
|
|
ctx.env
|
|
) {
|
|
try {
|
|
ctx.env.BARE_OS_BOOT_POLICY_DENY_BARE_RPC_PATTERNS_JSON =
|
|
JSON.stringify(pol.denyBareRpcMethodPatterns)
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.maxPearInspectDepth === 'number' &&
|
|
pol.maxPearInspectDepth >= 0 &&
|
|
ctx.env
|
|
) {
|
|
ctx.env.BARE_OS_BOOT_POLICY_MAX_PEAR_INSPECT_DEPTH = String(
|
|
Math.min(64, Math.floor(pol.maxPearInspectDepth))
|
|
)
|
|
}
|
|
if (
|
|
typeof pol.maxHrpcAllowlistDepth === 'number' &&
|
|
pol.maxHrpcAllowlistDepth >= 0 &&
|
|
ctx.env
|
|
) {
|
|
ctx.env.BARE_OS_BOOT_POLICY_MAX_HRPC_ALLOWLIST_DEPTH = String(
|
|
Math.min(64, Math.floor(pol.maxHrpcAllowlistDepth))
|
|
)
|
|
}
|
|
if (
|
|
typeof pol.requireBareLoggerMin === 'string' &&
|
|
String(pol.requireBareLoggerMin).trim()
|
|
) {
|
|
const needS = String(pol.requireBareLoggerMin).trim()
|
|
const haveS =
|
|
ctx.env && String(ctx.env.BARE_OS_BARE_LOGGER_VERSION || '').trim()
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireBareLoggerMin',
|
|
'[boot-policy] requireBareLoggerMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
Array.isArray(pol.denyAutobaseDiscoveryChannels) &&
|
|
ctx.env
|
|
) {
|
|
try {
|
|
ctx.env.BARE_OS_BOOT_POLICY_DENY_AUTOBASE_DISCOVERY_CHANNELS_JSON =
|
|
JSON.stringify(pol.denyAutobaseDiscoveryChannels)
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireBareTlsMin === 'string' &&
|
|
String(pol.requireBareTlsMin).trim()
|
|
) {
|
|
const needS = String(pol.requireBareTlsMin).trim()
|
|
const haveS =
|
|
ctx.env && String(ctx.env.BARE_OS_BARE_TLS_VERSION || '').trim()
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireBareTlsMin',
|
|
'[boot-policy] requireBareTlsMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireBarePackMin === 'string' &&
|
|
String(pol.requireBarePackMin).trim()
|
|
) {
|
|
const needS = String(pol.requireBarePackMin).trim()
|
|
const haveS =
|
|
ctx.env && String(ctx.env.BARE_OS_BARE_PACK_VERSION || '').trim()
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireBarePackMin',
|
|
'[boot-policy] requireBarePackMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
typeof pol.requireBareAddonPolicyMin === 'string' &&
|
|
String(pol.requireBareAddonPolicyMin).trim()
|
|
) {
|
|
const needS = String(pol.requireBareAddonPolicyMin).trim()
|
|
const haveS =
|
|
ctx.env &&
|
|
String(ctx.env.BARE_OS_BARE_ADDON_POLICY_VERSION || '').trim()
|
|
const ok = haveS && semverGte(haveS, needS)
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireBareAddonPolicyMin',
|
|
'[boot-policy] requireBareAddonPolicyMin not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
if (pol.offlineLkgRequirePearStamp === true && ctx.env) {
|
|
ctx.env.BARE_OS_OFFLINE_LKG_REQUIRE_PEAR_STAMP = '1'
|
|
}
|
|
if (pol.offlineLkgRequireHypercorePackHrpcLifecycle === true && ctx.env) {
|
|
ctx.env.BARE_OS_OFFLINE_LKG_REQUIRE_HYPERCORE_PACK_HRPC_LIFECYCLE = '1'
|
|
}
|
|
if (
|
|
typeof pol.offlineLkgManifestMaxAgeSec === 'number' &&
|
|
pol.offlineLkgManifestMaxAgeSec > 0 &&
|
|
ctx.env
|
|
) {
|
|
ctx.env.BARE_OS_OFFLINE_LKG_MANIFEST_MAX_AGE_SEC = String(
|
|
Math.min(1e9, Math.floor(pol.offlineLkgManifestMaxAgeSec))
|
|
)
|
|
}
|
|
if (
|
|
typeof pol.maxProtomuxChannelNameLength === 'number' &&
|
|
pol.maxProtomuxChannelNameLength >= 8 &&
|
|
ctx.env
|
|
) {
|
|
const n = Math.min(512, Math.floor(pol.maxProtomuxChannelNameLength))
|
|
ctx.env.BARE_OS_BOOT_POLICY_MAX_PROTO_MUX_CHANNEL_NAME_LENGTH = String(n)
|
|
}
|
|
if (
|
|
typeof pol.maxKernelExtensionDepth === 'number' &&
|
|
pol.maxKernelExtensionDepth >= 1 &&
|
|
ctx.env
|
|
) {
|
|
const d = Math.min(32, Math.floor(pol.maxKernelExtensionDepth))
|
|
ctx.env.BARE_OS_BOOT_POLICY_MAX_KERNEL_EXT_DEPTH = String(d)
|
|
}
|
|
if (
|
|
typeof pol.gitPartialClonePolicy === 'string' &&
|
|
String(pol.gitPartialClonePolicy).trim() &&
|
|
ctx.env
|
|
) {
|
|
ctx.env.BARE_OS_BOOT_POLICY_GIT_PARTIAL_CLONE = String(
|
|
pol.gitPartialClonePolicy
|
|
).trim()
|
|
}
|
|
if (
|
|
typeof pol.maxInitdRestartsPerUnit === 'number' &&
|
|
pol.maxInitdRestartsPerUnit >= 1 &&
|
|
ctx.env
|
|
) {
|
|
const cap = Math.min(32, Math.floor(pol.maxInitdRestartsPerUnit))
|
|
ctx.env.BARE_OS_INITD_RESTART_MAX_DEFAULT = String(cap)
|
|
}
|
|
if (
|
|
typeof pol.maxExecLineDepth === 'number' &&
|
|
pol.maxExecLineDepth > 0 &&
|
|
ctx.env
|
|
) {
|
|
const cap = Math.min(256, Math.floor(pol.maxExecLineDepth))
|
|
ctx.env.BARE_OS_EXEC_MAX_DEPTH = String(cap)
|
|
}
|
|
if (Array.isArray(pol.denyEnvKeys) && ctx.env) {
|
|
for (const k of pol.denyEnvKeys) {
|
|
const key = String(k).trim()
|
|
if (key && Object.prototype.hasOwnProperty.call(ctx.env, key)) {
|
|
delete ctx.env[key]
|
|
}
|
|
}
|
|
}
|
|
const needLifecycleSchema = Math.max(
|
|
typeof pol.bootStagesRequireLifecycleMinSchema === 'number'
|
|
? pol.bootStagesRequireLifecycleMinSchema
|
|
: 0,
|
|
typeof pol.bootPhasesRequireLifecycleMinSchema === 'number'
|
|
? pol.bootPhasesRequireLifecycleMinSchema
|
|
: 0
|
|
)
|
|
if (needLifecycleSchema > 0) {
|
|
const needSch = Math.floor(needLifecycleSchema)
|
|
const raw = ctx.env && String(ctx.env.BARE_OS_LIFECYCLE_SCHEMA_VERSION || '').trim()
|
|
const haveSch = Number.parseInt(raw, 10)
|
|
const ok = Number.isFinite(haveSch) && haveSch >= needSch
|
|
if (!ok) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'bootStagesRequireLifecycleMinSchema',
|
|
'[boot-policy] bootStagesRequireLifecycleMinSchema not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
const needProcIndexSchema = Math.max(
|
|
typeof pol.bootStagesRequireProcIndexMinSchema === 'number'
|
|
? pol.bootStagesRequireProcIndexMinSchema
|
|
: 0,
|
|
typeof pol.bootPhasesRequireProcIndexMinSchema === 'number'
|
|
? pol.bootPhasesRequireProcIndexMinSchema
|
|
: 0
|
|
)
|
|
if (needProcIndexSchema > 0) {
|
|
const vfs = ctx.vfs
|
|
const needSch = Math.floor(needProcIndexSchema)
|
|
if (vfs && typeof vfs.readFile === 'function') {
|
|
try {
|
|
const raw = await vfs.readFile('/proc/bare_os/index.json')
|
|
const txt =
|
|
raw && ctx.b4a && typeof ctx.b4a.toString === 'function'
|
|
? ctx.b4a.toString(raw)
|
|
: ''
|
|
const j = txt ? JSON.parse(txt) : null
|
|
const sch =
|
|
j && typeof j === 'object' && typeof j.schema === 'number'
|
|
? j.schema
|
|
: 0
|
|
if (sch < needSch) throw new Error('proc_index schema too old')
|
|
} catch {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'bootStagesRequireProcIndexMinSchema',
|
|
'[boot-policy] bootStagesRequireProcIndexMinSchema not satisfied'
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (Array.isArray(pol.requireProcNodes)) {
|
|
const vfs = ctx.vfs
|
|
if (vfs && typeof vfs.readFile === 'function') {
|
|
for (const rel of pol.requireProcNodes) {
|
|
const raw = String(rel).trim()
|
|
const p = raw.startsWith('/')
|
|
? raw
|
|
: '/proc/' + raw.replace(/^\/*/, '')
|
|
try {
|
|
const b = await vfs.readFile(p)
|
|
const empty =
|
|
!b ||
|
|
(ctx.b4a &&
|
|
typeof ctx.b4a.from === 'function' &&
|
|
ctx.b4a.from(b).length === 0)
|
|
if (empty) throw new Error('empty or missing')
|
|
} catch {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'requireProcNodes',
|
|
'[boot-policy] requireProcNodes not satisfied: ' + p
|
|
)
|
|
if (strictPol) {
|
|
if (typeof ctx.requestBooterExit === 'function') {
|
|
ctx.requestBooterExit(1)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
bootStructuredLog(
|
|
ctx,
|
|
'error',
|
|
'bootPolicyParse',
|
|
'[boot-policy] boot.policy.json: ' + ((e && e.message) || String(e))
|
|
)
|
|
}
|
|
return true
|
|
}
|