chore: auto-commit uncommitted changes

This commit is contained in:
James 2026-03-21 12:03:25 -04:00
parent af1ac0da44
commit a150ee226f
4 changed files with 95 additions and 0 deletions

View File

@ -426,6 +426,21 @@ func (h *Handlers) AuthLoginComplete(w http.ResponseWriter, r *http.Request) {
// ListEntries returns all entries (tree structure). // ListEntries returns all entries (tree structure).
func (h *Handlers) ListEntries(w http.ResponseWriter, r *http.Request) { func (h *Handlers) ListEntries(w http.ResponseWriter, r *http.Request) {
// Metadata-only mode: returns entry_id, type, title — no field data, no decryption.
// Used by web UI list view. Full data fetched per entry on click.
if r.URL.Query().Get("meta") == "1" {
entries, err := lib.EntryListMeta(h.db(r))
if err != nil {
ErrorResponse(w, http.StatusInternalServerError, "list_failed", "Failed to list entries")
return
}
if entries == nil {
entries = []lib.Entry{}
}
JSONResponse(w, http.StatusOK, entries)
return
}
actor := ActorFromContext(r.Context()) actor := ActorFromContext(r.Context())
var parent *int64 var parent *int64
if pidStr := r.URL.Query().Get("parent_id"); pidStr != "" { if pidStr := r.URL.Query().Get("parent_id"); pidStr != "" {

View File

@ -354,6 +354,29 @@ func EntryList(db *DB, vaultKey []byte, parentID *int64) ([]Entry, error) {
return entries, rows.Err() return entries, rows.Err()
} }
// EntryListMeta returns entry metadata only — no decryption, no field data.
// Used for list views. Individual entries fetched on demand via EntryGet.
func EntryListMeta(db *DB) ([]Entry, error) {
rows, err := db.Conn.Query(
`SELECT entry_id, parent_id, type, title, data_level, created_at, updated_at, version
FROM entries WHERE deleted_at IS NULL ORDER BY type, title`,
)
if err != nil {
return nil, err
}
defer rows.Close()
var entries []Entry
for rows.Next() {
var e Entry
if err := rows.Scan(&e.EntryID, &e.ParentID, &e.Type, &e.Title, &e.DataLevel, &e.CreatedAt, &e.UpdatedAt, &e.Version); err != nil {
return nil, err
}
entries = append(entries, e)
}
return entries, rows.Err()
}
// EntrySearch searches entries by title (blind index lookup). // EntrySearch searches entries by title (blind index lookup).
func EntrySearch(db *DB, vaultKey []byte, query string) ([]Entry, error) { func EntrySearch(db *DB, vaultKey []byte, query string) ([]Entry, error) {
hmacKey, err := DeriveHMACKey(vaultKey) hmacKey, err := DeriveHMACKey(vaultKey)

BIN
oss/app/vault1984-rXJEfw Normal file

Binary file not shown.

View File

@ -216,6 +216,63 @@
}); });
}); });
/* --- Test 13: L3 TOTP seed cannot be read with L2 key --- */
tests.push(function(done) {
var name = 'L3 TOTP seed inaccessible to agent (L2 key)';
var seed = 'JBSWY3DPEHPK3PXP'; // a TOTP seed
resolve(vault1984.crypto.encrypt_field(K32, 'totp', seed), function(ct, err) {
if (err) { fail(name, 'encrypt failed'); done(); return; }
// Agent has K16 (L2), tries to decrypt L3 TOTP seed
safe(function() { return vault1984.crypto.decrypt_field(K16, 'totp', ct); }, function(pt, err2) {
if (err2) pass(name);
else fail(name, 'L2 key decrypted L3 TOTP seed to "' + pt + '"');
done();
});
});
});
/* --- Test 14: L2 TOTP seed IS accessible with L2 key --- */
tests.push(function(done) {
var name = 'L2 TOTP seed accessible to agent (L2 key)';
var seed = 'JBSWY3DPEHPK3PXP';
resolve(vault1984.crypto.encrypt_field(K16, 'totp', seed), function(ct, err) {
if (err) { fail(name, 'encrypt failed'); done(); return; }
resolve(vault1984.crypto.decrypt_field(K16, 'totp', ct), function(pt, err2) {
if (err2) fail(name, err2.message);
else if (pt === seed) pass(name);
else fail(name, 'got "' + pt + '"');
done();
});
});
});
/* --- Test 15: L3 card number cannot be read with L2 key --- */
tests.push(function(done) {
var name = 'L3 card number inaccessible to agent (L2 key)';
resolve(vault1984.crypto.encrypt_field(K32, 'Number', '5452120017212208'), function(ct, err) {
if (err) { fail(name, 'encrypt failed'); done(); return; }
safe(function() { return vault1984.crypto.decrypt_field(K16, 'Number', ct); }, function(pt, err2) {
if (err2) pass(name);
else fail(name, 'L2 key decrypted L3 card number');
done();
});
});
});
/* --- Test 16: Truncation model — L2 key is prefix of L3 but cannot derive L3 --- */
tests.push(function(done) {
var name = 'truncation: L2 prefix of L3, still cannot decrypt L3';
// K16 = K32[0..16] by design. Encrypt with full K32, try with K16.
resolve(vault1984.crypto.encrypt_field(K32, 'secret', 'classified'), function(ct, err) {
if (err) { fail(name, 'encrypt failed'); done(); return; }
safe(function() { return vault1984.crypto.decrypt_field(K16, 'secret', ct); }, function(pt, err2) {
if (err2) pass(name);
else fail(name, 'L2 (prefix of L3) decrypted L3 data');
done();
});
});
});
/* --- Runner --- */ /* --- Runner --- */
function run(idx) { function run(idx) {
if (idx >= tests.length) { if (idx >= tests.length) {