chore: auto-commit uncommitted changes
This commit is contained in:
parent
af1ac0da44
commit
a150ee226f
|
|
@ -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 != "" {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue