Prepare sensitivity dependencies once per recorded operation
This commit is contained in:
@@ -21,10 +21,9 @@ type historySelection struct {
|
||||
}
|
||||
|
||||
type historySensitivityCandidate struct {
|
||||
path Path
|
||||
sensitive bool
|
||||
classify bool
|
||||
dependencies []Path
|
||||
path Path
|
||||
sensitive bool
|
||||
classify bool
|
||||
}
|
||||
|
||||
func (model *historyModel) selectHistory(path Path) []historySelection {
|
||||
@@ -198,28 +197,28 @@ func (model *historyModel) latestHistoricalLocation(path Path) (Location, bool)
|
||||
return Location{}, false
|
||||
}
|
||||
|
||||
func (model *historyModel) sensitivityCandidates() []historySensitivityCandidate {
|
||||
func (model *historyModel) sensitivityCandidates(inherited []secretDependency) []historySensitivityCandidate {
|
||||
if model == nil {
|
||||
return nil
|
||||
}
|
||||
candidates := make([]historySensitivityCandidate, 0, len(model.observations))
|
||||
indices := make(map[string][]int)
|
||||
add := func(path Path, sensitive, classify bool) int {
|
||||
seenAttributions := make(map[historyAttributionRef]struct{})
|
||||
add := func(path Path, sensitive, classify bool) {
|
||||
if !path.valid() {
|
||||
return -1
|
||||
return
|
||||
}
|
||||
pointer := path.String()
|
||||
for _, index := range indices[pointer] {
|
||||
if slices.Equal(candidates[index].path.segments, path.segments) {
|
||||
candidates[index].sensitive = candidates[index].sensitive || sensitive
|
||||
candidates[index].classify = candidates[index].classify || classify
|
||||
return index
|
||||
return
|
||||
}
|
||||
}
|
||||
index := len(candidates)
|
||||
candidates = append(candidates, historySensitivityCandidate{path: path.clone(), sensitive: sensitive, classify: classify})
|
||||
indices[pointer] = append(indices[pointer], index)
|
||||
return index
|
||||
}
|
||||
for _, observation := range model.observations {
|
||||
concreteCandidate := observation.role == historyRoleControl
|
||||
@@ -228,23 +227,22 @@ func (model *historyModel) sensitivityCandidates() []historySensitivityCandidate
|
||||
concreteCandidate = true
|
||||
}
|
||||
}
|
||||
index := add(observation.path, observation.sensitive, concreteCandidate)
|
||||
add(observation.path, observation.sensitive, concreteCandidate)
|
||||
if parsed, err := ParsePath(observation.path.String()); err == nil {
|
||||
add(parsed, observation.sensitive, true)
|
||||
}
|
||||
if index < 0 {
|
||||
if _, seen := seenAttributions[observation.attributionRef]; seen {
|
||||
continue
|
||||
}
|
||||
seenAttributions[observation.attributionRef] = struct{}{}
|
||||
if attribution, found := model.resolveAttribution(observation.attributionRef); found {
|
||||
for _, dependency := range attribution.dependencies {
|
||||
if dependency.valid() {
|
||||
appendDistinctHistoryPath(&candidates[index].dependencies, dependency)
|
||||
add(dependency, false, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_, inherited := model.sensitivityDependencies()
|
||||
for _, link := range inherited {
|
||||
add(link.target, false, true)
|
||||
add(link.dependency, false, true)
|
||||
@@ -252,15 +250,6 @@ func (model *historyModel) sensitivityCandidates() []historySensitivityCandidate
|
||||
return candidates
|
||||
}
|
||||
|
||||
func appendDistinctHistoryPath(paths *[]Path, path Path) {
|
||||
for _, existing := range *paths {
|
||||
if slices.Equal(existing.segments, path.segments) {
|
||||
return
|
||||
}
|
||||
}
|
||||
*paths = append(*paths, path.clone())
|
||||
}
|
||||
|
||||
// sensitivityDependencies returns the original attribution edges and the
|
||||
// bidirectional links represented by inheritance payloads. It intentionally
|
||||
// does not derive edges from the public metadata projection, which may omit
|
||||
@@ -269,10 +258,22 @@ func (model *historyModel) sensitivityDependencies() (dependencies, inherited []
|
||||
if model == nil {
|
||||
return nil, nil
|
||||
}
|
||||
// Every affected subtree fact borrows its operation's attribution. Expand
|
||||
// each logical target/dependency list once, not once for every descendant.
|
||||
type dependencyAttribution struct {
|
||||
ref historyAttributionRef
|
||||
target string
|
||||
}
|
||||
seen := make(map[dependencyAttribution]struct{})
|
||||
for _, observation := range model.observations {
|
||||
if observation.phase == historyPhaseInheritance {
|
||||
if observation.phase == historyPhaseInheritance || observation.attributionRef == noHistoryAttribution {
|
||||
continue
|
||||
}
|
||||
key := dependencyAttribution{ref: observation.attributionRef, target: observation.reportedPath.String()}
|
||||
if _, found := seen[key]; found {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
attribution, found := model.resolveAttribution(observation.attributionRef)
|
||||
if !found {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user