diff --git a/docs/config/plugins/github-com-jenkins-x-lighthouse-pkg-plugins.md b/docs/config/plugins/github-com-jenkins-x-lighthouse-pkg-plugins.md
index 7436d5cc7..d3f8f082e 100644
--- a/docs/config/plugins/github-com-jenkins-x-lighthouse-pkg-plugins.md
+++ b/docs/config/plugins/github-com-jenkins-x-lighthouse-pkg-plugins.md
@@ -206,6 +206,7 @@ Trigger specifies a configuration for a single trigger.
The configura
|---|---|---|---|
| `repos` | []string | No | Repos is either of the form org/repos or just org. |
| `trusted_org` | string | No | TrustedOrg is the org whose members' PRs will be automatically built
for PRs to the above repos. The default is the PR's org. |
+| `trusted_apps` | []string | No | TrustedApps is the explicit list of GitHub apps whose PRs will be automatically
considered as trusted. The list should contain usernames of each GitHub App without [bot] suffix.
By default, trigger will ignore this list. |
| `join_org_url` | string | No | JoinOrgURL is a link that redirects users to a location where they
should be able to read more about joining the organization in order
to become trusted members. Defaults to the Github link of TrustedOrg. |
| `only_org_members` | bool | No | OnlyOrgMembers requires PRs and/or /ok-to-test comments to come from org members.
By default, trigger also include repo collaborators. |
| `ignore_ok_to_test` | bool | No | IgnoreOkToTest makes trigger ignore /ok-to-test comments.
This is a security mitigation to only allow testing from trusted users. |
diff --git a/docs/plugins/Plugins config.md b/docs/plugins/Plugins config.md
index a82eb07e1..359e7e0e5 100644
--- a/docs/plugins/Plugins config.md
+++ b/docs/plugins/Plugins config.md
@@ -211,6 +211,7 @@ Trigger specifies a configuration for a single trigger.
The configura
|---|---|---|---|---|
| Repos | `repos` | []string | No | Repos is either of the form org/repos or just org. |
| TrustedOrg | `trusted_org` | string | No | TrustedOrg is the org whose members' PRs will be automatically built
for PRs to the above repos. The default is the PR's org. |
+| TrustedApps | `trusted_apps` | []string | No | TrustedApps is the explicit list of GitHub apps whose PRs will be automatically
considered as trusted. The list should contain usernames of each GitHub App without [bot] suffix.
By default, trigger will ignore this list. |
| JoinOrgURL | `join_org_url` | string | No | JoinOrgURL is a link that redirects users to a location where they
should be able to read more about joining the organization in order
to become trusted members. Defaults to the Github link of TrustedOrg. |
| OnlyOrgMembers | `only_org_members` | bool | No | OnlyOrgMembers requires PRs and/or /ok-to-test comments to come from org members.
By default, trigger also include repo collaborators. |
| IgnoreOkToTest | `ignore_ok_to_test` | bool | No | IgnoreOkToTest makes trigger ignore /ok-to-test comments.
This is a security mitigation to only allow testing from trusted users. |
diff --git a/pkg/plugins/config.go b/pkg/plugins/config.go
index e0529c1ae..e30674996 100644
--- a/pkg/plugins/config.go
+++ b/pkg/plugins/config.go
@@ -270,6 +270,10 @@ type Trigger struct {
// TrustedOrg is the org whose members' PRs will be automatically built
// for PRs to the above repos. The default is the PR's org.
TrustedOrg string `json:"trusted_org,omitempty"`
+ // TrustedApps is the explicit list of GitHub apps whose PRs will be automatically
+ // considered as trusted. The list should contain usernames of each GitHub App without [bot] suffix.
+ // By default, trigger will ignore this list.
+ TrustedApps []string `json:"trusted_apps,omitempty"`
// JoinOrgURL is a link that redirects users to a location where they
// should be able to read more about joining the organization in order
// to become trusted members. Defaults to the Github link of TrustedOrg.
diff --git a/pkg/plugins/trigger/trigger.go b/pkg/plugins/trigger/trigger.go
index 330d06305..5d4151f6a 100644
--- a/pkg/plugins/trigger/trigger.go
+++ b/pkg/plugins/trigger/trigger.go
@@ -186,7 +186,7 @@ func handlePush(pc plugins.Agent, pe scm.PushHook) error {
// TrustedUser returns true if user is trusted in repo.
//
-// Trusted users are either repo collaborators, org members or trusted org members.
+// Trusted users are either repo collaborators, org members, trusted org members or trusted Github Apps.
// Whether repo collaborators and/or a second org is trusted is configured by trigger.
func TrustedUser(spc trustedUserClient, trigger *plugins.Trigger, user, org, repo string) (bool, error) {
botUser, err := spc.BotName()
@@ -214,6 +214,14 @@ func TrustedUser(spc trustedUserClient, trigger *plugins.Trigger, user, org, rep
return true, nil
}
+ // Determine if user is on trusted_apps list.
+ // This allows automatic tests execution for GitHub automations that cannot be added as collaborators.
+ for _, trustedApp := range trigger.TrustedApps {
+ if tUser := strings.TrimSuffix(user, "[bot]"); tUser == trustedApp {
+ return true, nil
+ }
+ }
+
// Determine if there is a second org to check
if trigger.TrustedOrg == "" || trigger.TrustedOrg == org {
return false, nil // No trusted org and/or it is the same
diff --git a/pkg/plugins/trigger/trigger_test.go b/pkg/plugins/trigger/trigger_test.go
index 2e0489f02..f88851b6d 100644
--- a/pkg/plugins/trigger/trigger_test.go
+++ b/pkg/plugins/trigger/trigger_test.go
@@ -515,3 +515,128 @@ func TestValidateContextOverlap(t *testing.T) {
})
}
}
+
+func TestTrustedUser(t *testing.T) {
+ var testcases = []struct {
+ name string
+
+ onlyOrgMembers bool
+ trustedApps []string
+ trustedOrg string
+
+ user string
+ org string
+ repo string
+
+ expectedTrusted bool
+ }{
+ {
+ name: "user is member of trusted org",
+ onlyOrgMembers: false,
+ user: "test",
+ org: "kubernetes",
+ repo: "kubernetes",
+ expectedTrusted: true,
+ },
+ {
+ name: "user is member of trusted org (only org members enabled)",
+ onlyOrgMembers: true,
+ user: "test",
+ org: "kubernetes",
+ repo: "kubernetes",
+ expectedTrusted: true,
+ },
+ {
+ name: "user is collaborator",
+ onlyOrgMembers: false,
+ user: "test-collaborator",
+ org: "kubernetes",
+ repo: "kubernetes",
+ expectedTrusted: true,
+ },
+ {
+ name: "user is collaborator (only org members enabled)",
+ onlyOrgMembers: true,
+ user: "test-collaborator",
+ org: "kubernetes",
+ repo: "kubernetes",
+ expectedTrusted: false,
+ },
+ {
+ name: "user is trusted org member",
+ onlyOrgMembers: false,
+ trustedOrg: "kubernetes",
+ user: "test",
+ org: "kubernetes-sigs",
+ repo: "test",
+ expectedTrusted: true,
+ },
+ {
+ name: "user is not org member",
+ onlyOrgMembers: false,
+ user: "test-2",
+ org: "kubernetes",
+ repo: "kubernetes",
+ expectedTrusted: false,
+ },
+ {
+ name: "user is not org member or trusted org member",
+ onlyOrgMembers: false,
+ trustedOrg: "kubernetes-sigs",
+ user: "test-2",
+ org: "kubernetes",
+ repo: "kubernetes",
+ expectedTrusted: false,
+ },
+ {
+ name: "user is not org member or trusted org member, onlyOrgMembers true",
+ onlyOrgMembers: true,
+ trustedOrg: "kubernetes-sigs",
+ user: "test-2",
+ org: "kubernetes",
+ repo: "kubernetes",
+ expectedTrusted: false,
+ },
+ {
+ name: "Self as bot is trusted",
+ user: "k8s-ci-robot",
+ expectedTrusted: true,
+ },
+ {
+ name: "github-app[bot] is in trusted list",
+ user: "github-app[bot]",
+ trustedApps: []string{"github-app"},
+ expectedTrusted: true,
+ },
+ {
+ name: "github-app[bot] is not in trusted list",
+ user: "github-app[bot]",
+ trustedApps: []string{"other-app"},
+ expectedTrusted: false,
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.name, func(t *testing.T) {
+ fakeSCMClient := fake2.SCMClient{}
+ fakeSCMClient.OrgMembers = map[string][]string{
+ "kubernetes": {"test"},
+ }
+ fakeSCMClient.Collaborators = []string{"test-collaborator"}
+
+ triggerPlugin := plugins.Trigger{
+ TrustedOrg: tc.trustedOrg,
+ TrustedApps: tc.trustedApps,
+ OnlyOrgMembers: tc.onlyOrgMembers,
+ }
+
+ trustedResponse, err := TrustedUser(&fakeSCMClient, &triggerPlugin, tc.user, tc.org, tc.repo)
+ if err != nil {
+ t.Errorf("For case %s, didn't expect error from TrustedUser: %v", tc.name, err)
+ }
+ if trustedResponse != tc.expectedTrusted {
+ t.Errorf("For case %s, expect trusted: %v, but got: %v", tc.name, tc.expectedTrusted, trustedResponse)
+ }
+ })
+ }
+}