Files
bare-operating-system/packages/bare-os-coreutils/test/agent-helpers.test.mjs
T
2026-04-21 22:54:52 -04:00

100 lines
3.1 KiB
JavaScript

/**
* Mirrors semantics in lib/agent-helpers.js (apropos matching, mutate paths, proc allowlist).
* Keep aligned when editing helpers.
*/
import test from 'brittle'
/** @type {readonly string[]} */
const PROC_ALLOW = [
'/proc/bare_os/metrics_live.json',
'/proc/bare_os/features',
'/proc/bare_os/features.json',
'/proc/bare_os/swarm.json',
'/proc/bare_os/capabilities.json',
'/proc/bare_os/swarm_connection_manager_status.json'
]
function pathAllowedMutate(p) {
const x = String(p || '').replace(/\\/g, '/')
if (!x.startsWith('/') || x.includes('..')) return false
return (
x.startsWith('/home/') ||
x.startsWith('/tmp/') ||
x === '/tmp' ||
x.startsWith('/root/') ||
x.startsWith('/mnt/')
)
}
function procAllowed(p) {
const x = String(p || '').replace(/\\/g, '/')
if (!x.startsWith('/') || x.includes('..')) return false
return PROC_ALLOW.includes(x)
}
function aproposHits(db, needle, maxHits) {
const n = String(needle || '').toLowerCase()
const max = Math.min(Math.max(Math.floor(maxHits) || 40, 1), 200)
if (!n || !db || typeof db !== 'object') return { lines: [], truncated: false }
const d = /** @type {Record<string, unknown>} */ (db)
const pages = Array.isArray(d.pages) ? d.pages : []
const apropos = Array.isArray(d.apropos) ? d.apropos : []
const seen = new Set()
/** @type {string[]} */
const lines = []
let truncated = false
for (const row of apropos) {
if (!row || typeof row !== 'object') continue
const kw = typeof row.kw === 'string' ? row.kw : ''
if (!kw.includes(n)) continue
const idx = row.pageRef
if (typeof idx !== 'number' || !pages[idx]) continue
if (seen.has(idx)) continue
seen.add(idx)
const p = /** @type {Record<string, unknown>} */ (pages[idx])
const name = typeof p.name === 'string' ? p.name : ''
const sec = typeof p.section === 'number' ? p.section : 0
const title = typeof p.title === 'string' ? p.title : ''
lines.push(name + '(' + sec + ') - ' + title)
if (lines.length >= max) {
truncated = true
break
}
}
lines.sort()
return { lines, truncated }
}
test('mutate paths allow guest areas only', async (t) => {
t.ok(pathAllowedMutate('/home/guest/x'))
t.ok(pathAllowedMutate('/tmp/a'))
t.absent(pathAllowedMutate('/bin/foo'))
t.absent(pathAllowedMutate('/etc/passwd'))
t.absent(pathAllowedMutate('/proc/x'))
})
test('proc allowlist exact paths', async (t) => {
t.ok(procAllowed('/proc/bare_os/features'))
t.ok(procAllowed('/proc/bare_os/swarm.json'))
t.ok(procAllowed('/proc/bare_os/swarm_connection_manager_status.json'))
t.absent(procAllowed('/proc/bare_os/other.json'))
t.absent(procAllowed('/proc/../proc/bare_os/swarm.json'))
})
test('apropos substring matches kw like man -k', async (t) => {
const db = {
pages: [
{ name: 'grep', section: 1, title: 'pattern search' },
{ name: 'true', section: 1, title: 'exit successfully' }
],
apropos: [
{ kw: 'grep', pageRef: 0 },
{ kw: 'pattern', pageRef: 0 },
{ kw: 'true', pageRef: 1 }
]
}
const r = aproposHits(db, 'pat', 10)
t.is(r.lines.length, 1)
t.ok(r.lines[0].startsWith('grep('))
})