Files
safetynet/tests/bookmarks-native.test.mjs
T
2026-10-13 10:34:48 +02:00

14 lines
3.6 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import bookmarks from '../apps/browser/bookmarks.cjs';
import settings from '../apps/browser/managed-settings.cjs';
const fixture=()=>{const policy={version:3,expiresAt:new Date(Date.now()+60000).toISOString(),browser:{settings:{visible:true,hiddenSections:[],lockedSections:[]},bookmarks:{managed:[{id:'company',title:'Company',url:'https://intranet.example/',folder:''}],allowUserAdd:true,allowUserEdit:true,allowUserDelete:true,personal:{revision:2,items:[{id:'mine',title:'Mine',url:'https://example.com/',folder:''}]}}}};return {policy,ready:true,settings:settings.settingsState({policy,ready:true}),destinationAllowed:url=>new URL(url).hostname==='example.com'};};
const add=()=>({action:'add',expectedRevision:2,policyVersion:3,bookmark:{id:'new',title:' Example ',url:'https://example.com/',folder:''}});
test('bookmark mutation binds signed policy and personal revision',()=>{const context=fixture();assert.equal(bookmarks.validateMutation(add(),context).bookmark.title,'Example');for(const input of [{...add(),policyVersion:2},{...add(),expectedRevision:1}])assert.throws(()=>bookmarks.validateMutation(input,context));});
test('managed bookmark ids cannot be changed through direct native calls',()=>{for(const action of ['add','update','delete']){const input={...add(),action,id:'company',bookmark:{...add().bookmark,id:'company'}};assert.throws(()=>bookmarks.validateMutation(input,fixture()),/Company bookmarks/);}});
test('independent add edit delete permissions are enforced natively',()=>{for(const [action,permission] of [['add','allowUserAdd'],['update','allowUserEdit'],['delete','allowUserDelete']]){const context=fixture();context.policy.browser.bookmarks[permission]=false;assert.throws(()=>bookmarks.validateMutation({...add(),action,id:'mine',bookmark:{...add().bookmark,id:'mine'}},context),/administrator/);}});
test('hidden locked expired and paused settings reject native mutations',()=>{for(const mode of ['hidden','locked','expired','paused']){const context=fixture();if(mode==='hidden')context.settings.hiddenSections=['library'];if(mode==='locked')context.settings.lockedSections=['library'];if(mode==='expired')context.policy.expiresAt='2000-01-01';if(mode==='paused')context.ready=false;assert.throws(()=>bookmarks.validateMutation(add(),context));}});
test('bookmark URLs cannot bypass destination controls or retain sensitive queries',()=>{for(const url of ['javascript:alert(1)','https://other.example/','https://a:[email protected]/','https://example.com/?token=x','https://example.com/#private'])assert.throws(()=>bookmarks.validateMutation({...add(),bookmark:{...add().bookmark,url}},fixture()));});
test('locked library retains company links; hidden library exposes no bookmark metadata',()=>{const context=fixture();context.settings.lockedSections=['library'];assert.equal(bookmarks.bookmarkState(context).managed.length,1);context.settings.hiddenSections=['library'];assert.deepEqual(bookmarks.bookmarkState(context).managed,[]);});
test('global settings hiding keeps bookmark navigation readable but forbids changes',()=>{const context=fixture();context.settings.visible=false;const state=bookmarks.bookmarkState(context);assert.equal(state.managed.length,1);assert.equal(state.personal.items.length,1);assert.equal(state.allowUserAdd,false);assert.equal(bookmarks.libraryVisible(context.settings),true);assert.throws(()=>bookmarks.validateMutation(add(),context));context.settings.hiddenSections=['library'];assert.equal(bookmarks.libraryVisible(context.settings),false);assert.deepEqual(bookmarks.bookmarkState(context).personal.items,[]);});