Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions modules/jarvos-agent-context/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,30 @@ function routeThreadKey(input = {}) {
// Defaults to the bundled modules copy; set JARVOS_SECONDBRAIN_DIR to an absolute
// path (e.g. the canonical clawd mirror) to point all note-creation through it.
function secondbrainDir() {
return expandTilde(process.env.JARVOS_SECONDBRAIN_DIR)
|| path.join(JARVOS_ROOT, 'modules', 'jarvos-secondbrain');
const configured = expandTilde(process.env.JARVOS_SECONDBRAIN_DIR);
const candidate = configured || path.join(JARVOS_ROOT, 'modules', 'jarvos-secondbrain');
if (!path.isAbsolute(candidate)) {
throw new Error('JARVOS_SECONDBRAIN_DIR must be an absolute path');
}

let trustedRoot;
try {
trustedRoot = fs.realpathSync(candidate);
} catch {
throw new Error('JARVOS_SECONDBRAIN_DIR must identify an existing directory');
}

const uid = typeof process.getuid === 'function' ? process.getuid() : null;
for (let current = trustedRoot; ; current = path.dirname(current)) {
const stat = fs.statSync(current);
const trustedOwner = uid === null || stat.uid === uid || stat.uid === 0;
if (!stat.isDirectory() || !trustedOwner || (stat.mode & 0o022) !== 0) {
throw new Error('JARVOS_SECONDBRAIN_DIR must be in an owner-controlled directory tree');
}
const parent = path.dirname(current);
if (parent === current) break;
}
return trustedRoot;
}

function loadJarvosPaths() {
Expand Down
14 changes: 14 additions & 0 deletions modules/jarvos-agent-context/test/agent-context.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,20 @@ test('createNote writes note, links journal, and verifies contract', () => {
});
});

test('createNote rejects an untrusted secondbrain module root before loading code', () => {
const previous = process.env.JARVOS_SECONDBRAIN_DIR;
process.env.JARVOS_SECONDBRAIN_DIR = 'relative/attacker-controlled-secondbrain';
try {
assert.throws(
() => createNote({ title: 'Unsafe module root', content: 'Must not execute.' }),
/JARVOS_SECONDBRAIN_DIR must be an absolute path/,
);
} finally {
if (previous === undefined) delete process.env.JARVOS_SECONDBRAIN_DIR;
else process.env.JARVOS_SECONDBRAIN_DIR = previous;
}
});

test('createNote creates today journal when missing', () => {
withTempVault(({ journal, mutationService }) => {
const result = createNote({
Expand Down
Loading