Skip to content

Commit 9a555bc

Browse files
committed
feat: org setting to block attestations on released versions
Add an opt-in organization-level setting, block_attestations_on_released_versions, that rejects new attestations targeting project versions that are already released (prerelease == false). Default is false, preserving current behavior. Enforcement happens at two points: at attestation init, transactionally with a row lock when resolving the project version, providing fail-fast feedback before any work is done; and at push in SaveAttestation, which acts as the authoritative gate closing the window where a version could be released between init and push. Both return a dedicated typed error mapped to a FailedPrecondition gRPC code so the CLI surfaces a clear message. The setting is exposed through the organization settings API and the chainloop organization update CLI command. Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: 146f4ecb-bdc9-4562-8b1b-2481ea10d7bf
1 parent 09a2e99 commit 9a555bc

37 files changed

Lines changed: 570 additions & 53 deletions

app/cli/cmd/organization_update.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ import (
2525

2626
func newOrganizationUpdateCmd() *cobra.Command {
2727
var (
28-
orgName string
29-
blockOnPolicyViolation bool
30-
policiesAllowedHostnames []string
31-
preventImplicitWorkflowCreation bool
32-
restrictContractCreation bool
33-
apiTokenMaxDaysInactive string
34-
enableAIAgentCollector bool
28+
orgName string
29+
blockOnPolicyViolation bool
30+
policiesAllowedHostnames []string
31+
preventImplicitWorkflowCreation bool
32+
restrictContractCreation bool
33+
apiTokenMaxDaysInactive string
34+
enableAIAgentCollector bool
35+
blockAttestationsOnReleasedVersions bool
3536
)
3637

3738
cmd := &cobra.Command{
@@ -59,6 +60,10 @@ func newOrganizationUpdateCmd() *cobra.Command {
5960
opts.EnableAIAgentCollector = &enableAIAgentCollector
6061
}
6162

63+
if cmd.Flags().Changed("block-attestations-on-released-versions") {
64+
opts.BlockAttestationsOnReleasedVersions = &blockAttestationsOnReleasedVersions
65+
}
66+
6267
if cmd.Flags().Changed("api-token-max-days-inactive") {
6368
days, err := strconv.Atoi(apiTokenMaxDaysInactive)
6469
if err != nil {
@@ -90,5 +95,6 @@ func newOrganizationUpdateCmd() *cobra.Command {
9095
cmd.Flags().BoolVar(&restrictContractCreation, "restrict-contract-creation", false, "restrict contract creation (org-level and project-level) to only organization admins (owner/admin roles)")
9196
cmd.Flags().StringVar(&apiTokenMaxDaysInactive, "api-token-max-days-inactive", "", "maximum days of inactivity before API tokens are auto-revoked (e.g. '90', '0' to disable)")
9297
cmd.Flags().BoolVar(&enableAIAgentCollector, "enable-ai-agent-collector", false, "enable automatic AI agent config collection during attestation init")
98+
cmd.Flags().BoolVar(&blockAttestationsOnReleasedVersions, "block-attestations-on-released-versions", false, "reject new attestations pushed to project versions that are already released")
9399
return cmd
94100
}

app/cli/documentation/cli-reference.mdx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2908,14 +2908,15 @@ chainloop organization update [flags]
29082908
Options
29092909

29102910
```
2911-
--api-token-max-days-inactive string maximum days of inactivity before API tokens are auto-revoked (e.g. '90', '0' to disable)
2912-
--block set the default policy violation blocking strategy
2913-
--enable-ai-agent-collector enable automatic AI agent config collection during attestation init
2914-
-h, --help help for update
2915-
--name string organization name
2916-
--policies-allowed-hostnames strings set the allowed hostnames for the policy engine
2917-
--prevent-implicit-workflow-creation prevent workflows and projects from being created implicitly during attestation init
2918-
--restrict-contract-creation restrict contract creation (org-level and project-level) to only organization admins (owner/admin roles)
2911+
--api-token-max-days-inactive string maximum days of inactivity before API tokens are auto-revoked (e.g. '90', '0' to disable)
2912+
--block set the default policy violation blocking strategy
2913+
--block-attestations-on-released-versions reject new attestations pushed to project versions that are already released
2914+
--enable-ai-agent-collector enable automatic AI agent config collection during attestation init
2915+
-h, --help help for update
2916+
--name string organization name
2917+
--policies-allowed-hostnames strings set the allowed hostnames for the policy engine
2918+
--prevent-implicit-workflow-creation prevent workflows and projects from being created implicitly during attestation init
2919+
--restrict-contract-creation restrict contract creation (org-level and project-level) to only organization admins (owner/admin roles)
29192920
```
29202921

29212922
Options inherited from parent commands

app/cli/pkg/action/membership_list.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ type MembershipList struct {
2929
}
3030

3131
type OrgItem struct {
32-
ID string `json:"id"`
33-
Name string `json:"name"`
34-
CreatedAt *time.Time `json:"createdAt"`
35-
PolicyViolationBlockingStrategy string `json:"policyViolationBlockingStrategy"`
36-
PolicyAllowedHostnames []string `json:"policyAllowedHostnames,omitempty"`
37-
PreventImplicitWorkflowCreation bool `json:"preventImplicitWorkflowCreation"`
38-
APITokenMaxDaysInactive *string `json:"apiTokenMaxDaysInactive,omitempty"`
39-
EnableAIAgentCollector bool `json:"enableAiAgentCollector"`
32+
ID string `json:"id"`
33+
Name string `json:"name"`
34+
CreatedAt *time.Time `json:"createdAt"`
35+
PolicyViolationBlockingStrategy string `json:"policyViolationBlockingStrategy"`
36+
PolicyAllowedHostnames []string `json:"policyAllowedHostnames,omitempty"`
37+
PreventImplicitWorkflowCreation bool `json:"preventImplicitWorkflowCreation"`
38+
APITokenMaxDaysInactive *string `json:"apiTokenMaxDaysInactive,omitempty"`
39+
EnableAIAgentCollector bool `json:"enableAiAgentCollector"`
40+
BlockAttestationsOnReleasedVersions bool `json:"blockAttestationsOnReleasedVersions"`
4041
}
4142

4243
type MembershipItem struct {
@@ -134,12 +135,13 @@ func (action *MembershipList) ListMembers(ctx context.Context, page int, pageSiz
134135

135136
func pbOrgItemToAction(in *pb.OrgItem) *OrgItem {
136137
i := &OrgItem{
137-
ID: in.Id,
138-
Name: in.Name,
139-
CreatedAt: toTimePtr(in.CreatedAt.AsTime()),
140-
PolicyAllowedHostnames: in.PolicyAllowedHostnames,
141-
PreventImplicitWorkflowCreation: in.PreventImplicitWorkflowCreation,
142-
EnableAIAgentCollector: in.EnableAiAgentCollector,
138+
ID: in.Id,
139+
Name: in.Name,
140+
CreatedAt: toTimePtr(in.CreatedAt.AsTime()),
141+
PolicyAllowedHostnames: in.PolicyAllowedHostnames,
142+
PreventImplicitWorkflowCreation: in.PreventImplicitWorkflowCreation,
143+
EnableAIAgentCollector: in.EnableAiAgentCollector,
144+
BlockAttestationsOnReleasedVersions: in.BlockAttestationsOnReleasedVersions,
143145
}
144146

145147
if in.DefaultPolicyViolationStrategy == pb.OrgItem_POLICY_VIOLATION_BLOCKING_STRATEGY_BLOCK {

app/cli/pkg/action/org_update.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ type NewOrgUpdateOpts struct {
4040
APITokenMaxDaysInactive *int
4141
// EnableAIAgentCollector enables automatic AI agent config collection during attestation init
4242
EnableAIAgentCollector *bool
43+
// BlockAttestationsOnReleasedVersions rejects new attestations pushed to project versions that are already released
44+
BlockAttestationsOnReleasedVersions *bool
4345
}
4446

4547
func (action *OrgUpdate) Run(ctx context.Context, name string, opts *NewOrgUpdateOpts) (*OrgItem, error) {
@@ -51,6 +53,7 @@ func (action *OrgUpdate) Run(ctx context.Context, name string, opts *NewOrgUpdat
5153
PreventImplicitWorkflowCreation: opts.PreventImplicitWorkflowCreation,
5254
RestrictContractCreationToOrgAdmins: opts.RestrictContractCreation,
5355
EnableAiAgentCollector: opts.EnableAIAgentCollector,
56+
BlockAttestationsOnReleasedVersions: opts.BlockAttestationsOnReleasedVersions,
5457
}
5558

5659
if opts.PoliciesAllowedHostnames != nil {

app/controlplane/api/controlplane/v1/organization.pb.go

Lines changed: 16 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/controlplane/api/controlplane/v1/organization.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ message OrganizationServiceUpdateRequest {
105105

106106
// Enable automatic AI agent config collection during attestation init
107107
optional bool enable_ai_agent_collector = 8;
108+
109+
// Reject new attestations pushed to project versions that are already released (prerelease == false)
110+
optional bool block_attestations_on_released_versions = 9;
108111
}
109112

110113
message OrganizationServiceUpdateResponse {

app/controlplane/api/controlplane/v1/response_messages.pb.go

Lines changed: 14 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/controlplane/api/controlplane/v1/response_messages.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,8 @@ message OrgItem {
391391
optional int32 api_token_max_days_inactive = 9;
392392
// Whether AI agent config collection is automatically enabled during attestation init
393393
bool enable_ai_agent_collector = 10;
394+
// Whether new attestations are rejected on project versions that are already released (prerelease == false)
395+
bool block_attestations_on_released_versions = 11;
394396

395397
enum PolicyViolationBlockingStrategy {
396398
POLICY_VIOLATION_BLOCKING_STRATEGY_UNSPECIFIED = 0;

app/controlplane/api/gen/frontend/controlplane/v1/organization.ts

Lines changed: 22 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/controlplane/api/gen/frontend/controlplane/v1/response_messages.ts

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)