Bind trusted task approval to exact producer facts and lifetime
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
parent
4f6946bbb3
commit
2b064eee51
31 changed files with 2170 additions and 30 deletions
36
internal/registry/numeric_source_test.go
Normal file
36
internal/registry/numeric_source_test.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTaskRegistryNumericSourceAndStrictDecode(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "snapshot.json")
|
||||
raw := []byte(`{"resource_manifests":[{"id":"synthetic","system":"secrets-engine","resources":[{"id":"catalog:synthetic","type":"secret-catalog-lane","attributes":{"float":1.0,"integer":9007199254740993,"negative_zero":-0.0}}]}]}`)
|
||||
os.WriteFile(path, raw, 0600)
|
||||
store, err := LoadFilePreservingNumbers(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !store.NumbersPreserved() {
|
||||
t.Fatal("missing source profile")
|
||||
}
|
||||
resource, ok := store.Resource("secrets-engine", "catalog:synthetic")
|
||||
if !ok {
|
||||
t.Fatal("missing resource")
|
||||
}
|
||||
for k, v := range map[string]string{"float": "1.0", "integer": "9007199254740993", "negative_zero": "-0.0"} {
|
||||
if resource.Attributes[k] != json.Number(v) {
|
||||
t.Fatalf("lost native representation: %s", k)
|
||||
}
|
||||
}
|
||||
for _, raw := range []string{`{} {}`, `{"subjects":[],"subjects":[]}`, `{"unknown":true}`} {
|
||||
os.WriteFile(path, []byte(raw), 0600)
|
||||
if _, err := LoadFilePreservingNumbers(path); err == nil {
|
||||
t.Fatal("invalid authoritative registry accepted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
|
@ -8,10 +9,12 @@ import (
|
|||
"sort"
|
||||
|
||||
"github.com/netkingdom/access-engine/pkg/api"
|
||||
"github.com/netkingdom/approval-engine/adapters/go/approvalfact"
|
||||
)
|
||||
|
||||
// Store is a deterministic local registry for standalone development and tests.
|
||||
type Store struct {
|
||||
numbersPreserved bool
|
||||
systems map[string]api.ProtectedSystemManifest
|
||||
resourceManifests map[string]api.ResourceManifest
|
||||
resources map[string]api.Resource
|
||||
|
|
@ -66,6 +69,32 @@ func LoadFile(path string) (*Store, error) {
|
|||
return store, nil
|
||||
}
|
||||
|
||||
// LoadFilePreservingNumbers is the task-source profile: owner registry numeric
|
||||
// representations stay intact for native act correspondence. Ordinary loaders
|
||||
// and their existing snapshot/digest behavior remain unchanged.
|
||||
func LoadFilePreservingNumbers(path string) (*Store, error) {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := approvalfact.DecodeJSON(raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var snapshot Snapshot
|
||||
decoder := json.NewDecoder(bytes.NewReader(raw))
|
||||
decoder.UseNumber()
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&snapshot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
store := NewStore()
|
||||
if err := store.ApplySnapshot(snapshot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
store.numbersPreserved = true
|
||||
return store, nil
|
||||
}
|
||||
|
||||
// SaveFile writes the store as deterministic pretty-printed JSON.
|
||||
func (s *Store) SaveFile(path string) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
|
|
@ -380,3 +409,6 @@ func sortedValues[T any](items map[string]T) []T {
|
|||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// NumbersPreserved reports the source profile of file-backed registry facts.
|
||||
func (s *Store) NumbersPreserved() bool { return s != nil && s.numbersPreserved }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue