Scope inherited policies against their own configuration
🤖 AI wrote this code from my instructions.Unreviewed AI output often lands on reviewers and costs them time.
This MR is human verified: I read the full diff and ran the tests myself, because your time is as valuable as mine.
What does this MR do and why?
Security::SecurityPolicyBaseFinder#merge_project_relationship receives one security orchestration policy configuration, iterates the policies that belong to it, then decides for each whether it applies to the object being queried. The scope check got the wrong configuration:
policies(config).filter_map do |policy|
next if !params[:include_unscoped] && !policy_scope_applicable?(policy, policy_configuration)policy_configuration is the object's own configuration. config is the configuration the policy came from, which may be an inherited group configuration, a descendant configuration, or a CSP configuration. The receiving method already names its parameter config.
The argument reaches PolicyScopeChecker#security_attributes_scope_enabled?, which does return false unless configuration and then configuration.experiment_enabled?(:security_attributes_policy_scope). A project with no configuration of its own that inherits group policies has policy_configuration set to nil. The check returns false, so every business_impact, application, business_unit and exposure scope on an inherited policy is skipped. The policy is then reported as applicable to a project it was scoped away from.
The fix passes config. The sibling entry point in the same checker already does this: security_policy_applicable? reads the configuration from security_policy.security_orchestration_policy_configuration.
This is visible only when the security_attributes_policy_scope flag is on for the group (it is default_enabled: true) and the configuration has the experiment enabled. The direction of the bug is permissive: policies apply where they should not. No new feature flag and no migration.
A new context in the scan execution policies finder spec builds a project with no configuration of its own that inherits a group policy scoped to a business_impact attribute. One example asserts the policy is excluded when the project does not carry the attribute. A second asserts it is included when the project does carry it, as a control.
References
None.
How to set up and validate locally
This has no simple console reproduction, since it needs security attributes and the experiment enabled. Run the spec instead.
bundle exec rspec ee/spec/finders/security/scan_execution_policies_finder_spec.rb28 examples, 0 failures.
Revert the one-line change in merge_project_relationship and run it again:
1) Security::ScanExecutionPoliciesFinder when an inherited policy is scoped by a
security attribute excludes the policy when the project does not carry the attribute
Failure/Error: is_expected.to be_empty
expected `[{:actions=>[{:scan=>"dast", ...}], :type=>"scan_execution_policy"}].empty?`
to be truthy, got falseThe returned policy is the one the project's security attributes should have excluded.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.