Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: Change Branch-Protection check to consider rule of "change only through PRs" #2727

Closed
diogoteles08 opened this issue Mar 7, 2023 · 5 comments · Fixed by #3499
Labels
kind/enhancement New feature or request

Comments

@diogoteles08
Copy link
Contributor

diogoteles08 commented Mar 7, 2023

Is your feature request related to a problem? Please describe.

The current Branch Protection check does not consider the rule named Require a pull request before merging (you can check it on the GitHub page). This rule is actually very important because it forces any change on the branch to be made through PRs, and would therefore prevent malicious pushes -- that could even be made through actions with write permissions.

The closest to this we currently have is the requirement of Reviewers >= 1 to get a 6/10 score. But this might be specially difficult to solo-devs, while the Require a pull request before merging is possible even for a single maintainer.

Describe the solution you'd like

The purpose is to make this rule enhance the Branch-Protection rule somehow. My suggestion would be to add this rule as a new tier with a 5/10 punctuation. This would give a score higher than the 3/10, which basically requires prevention of force pushes and branch deletion, and slightly lower than 6/10, which requires a reviewer other than the commiter.

Describe alternatives you've considered

  • The rule could be added as a new requirement to get a 3/10 grade. But this might be frustrating to the projects that already had the effort to get this grade, as they could be led to 0/10.
@diogoteles08 diogoteles08 added the kind/enhancement New feature or request label Mar 7, 2023
@diogoteles08
Copy link
Contributor Author

Is there any opposition to this? If not, feel free to assign me to it

@spencerschrock
Copy link
Member

Overall, I could see this being some sort of partial credit. I've left some comments inline.

Is your feature request related to a problem? Please describe.

The current Branch Protection check does not consider the rule named Require a pull request before merging (you can check it on the GitHub page). This rule is actually very important because it forces any change on the branch to be made through PRs, and would therefore prevent malicious pushes -- that could even be made through actions with write permissions.

The closest to this we currently have is the requirement of Reviewers >= 1 to get a 6/10 score. But this might be specially difficult to solo-devs, while the Require a pull request before merging is possible even for a single maintainer.

I think "Require branch to pass at least 1 status check before merging" also captures this to some extent. Of course this has the issue of tiers (which you raised in #3123).
Also some of the admin level tier 2 points indirectly hit on this, which might become more visible after repo rules are supported (#3326).

Describe the solution you'd like

The purpose is to make this rule enhance the Branch-Protection rule somehow. My suggestion would be to add this rule as a new tier with a 5/10 punctuation. This would give a score higher than the 3/10, which basically requires prevention of force pushes and branch deletion, and slightly lower than 6/10, which requires a reviewer other than the commiter.

I think the code review is a much more important bit, but I could see requiring a PR to possibly be worth a single point.

Describe alternatives you've considered

  • The rule could be added as a new requirement to get a 3/10 grade. But this might be frustrating to the projects that already had the effort to get this grade, as they could be led to 0/10.

This could also be part of the tier 1 if #3123 is changed.

@diogoteles08
Copy link
Contributor Author

diogoteles08 commented Aug 23, 2023

One important detail that I discovered:
By my testings, the Require a pull request before merging seems only to be retrievable when using an admin token :/
I concluded this because running Scorecard in a repository with this option disable, using an Admin token, I get

BranchProtectionRule.RequiredPullRequestReviews = {
    RequiredApprovingReviewCount: *int32 nil,
    DismissStaleReviews: *false,
    RequireCodeOwnerReviews: *false
}

, but if I run without the admin token, for the exact same repository without changing the configs, I get

BranchProtectionRule.RequiredPullRequestReviews = {
    RequiredApprovingReviewCount: *int32 0,
    DismissStaleReviews: *false,
    RequireCodeOwnerReviews: *false
}

, which is the exact same result that we get when the repository requires PRs, but doesn't require a reviewer. This way, we could not differentiate a repository that does not requires PRs to a repository that requires PRs but doesn't require another reviewer.

Obs: those values were gotten inside the run of Scorecard as values available on the evaluation part of the run. This could be fact-checked by looking into the complete response of Github's API.

EDIT: I was able to verify directly using the GitHub's API and the problem still applies. To test, I used the GraphQL explorer and ran the following query:

{
  repository(owner: "diogoteles08", name: "testing-branch-protections") {
    defaultBranchRef {
      name
      refUpdateRule {
        requiredApprovingReviewCount
      }
    }
  }
}

In any ways, I think this feature is still important, especially thinking that after repo rules support (#3326) all branch protection configuration should be public.

@diogoteles08
Copy link
Contributor Author

After private discussions with @spencerschrock , I've came up with this idea on how to add this rule on the current Branch Protection score:

We'd add this as another Tier-2 requirement, but as the "Require 1 reviewer" has a stronger security impact, it'll have a bigger weight over the other Tier-2 requirements.

On the current implementation of this check we compute the score of each tier using the concept of a total point per tier (which currently is the number of requirements for the tier). Then we keep adding points as the branch matches the rules (i.e. + 1 for having 1 reviewer and + 1 for requiring review of most recent push) and then make the score for the tier as the summed points / total point per tier * maximum score for this tier. The latter is always 3 for the Tier 2, as it's the score added on the Branch Protection check when you fully comply with the Tier-2 requirements

Currently the Tier 2 requirements are like this:

"total point per tier" = 3 if run with admin, otherwise "total point per tier" = 1

+1 point for Require at least 1 reviewer for approval before merging
+1 point for Require branch to be up to date before merging (admin only)
+1 point for Require approval of the most recent reviewable push (admin only)

My idea of implementation would change this to:

"total point per tier" = 5 if run with admin, otherwise "total point per tier" = 2

+1 point for Require a pull request before merging (admin only)
+2 point for Require at least 1 reviewer for approval before merging
+1 point for Require branch to be up to date before merging (admin only)
+1 point for Require approval of the most recent reviewable push (admin only)

Analysis on use cases

Running without admin permissions:

  • Someone using scorecard without admin permissions would only need "Require at least 1 reviewer for approval before merging" to complete Tier 2 requirements. The same occurred before this PR.

Running with admin permissions (or after Scorecard support for Rule Set #3326):

  • A solo-maintainer could match all Tier 2 requirements except "Require at least 1 reviewer for approval before merging" and he would get 3/5 * 3 = 1.8 as the Tier 2 score. Which means that he could get a maximum score of 4.8/10 on Branch protection
  • A non-solo-maintainer that complies "Require at least 1 reviewer for approval before merging", would automatically comply with "Require a pull request before merging". So if they for any reason don't want to comply with the other Tier 2 requirements, they'd already have a 3/5 * 3 = 1.8 as the Tier 2 score -- leading to a 4.8/10 score on Branch Protection

diogoteles08 added a commit to diogoteles08/scorecard that referenced this issue Sep 19, 2023
…ke changes

As discussed at the issue ossf#2727, we're adding the "require PRs prior
to make changes" as another requirement to tier 2. In addition to that,
we're changing the weight of the tier 2 requirements so that
"requiring 1 reviewer" has weight 2, while the other tier 2 requirements
have weight 1
diogoteles08 added a commit to diogoteles08/scorecard that referenced this issue Sep 20, 2023
…ke changes

As discussed at the issue ossf#2727, we're adding the "require PRs prior
to make changes" as another requirement to tier 2. In addition to that,
we're changing the weight of the tier 2 requirements so that
"requiring 1 reviewer" has weight 2, while the other tier 2 requirements
have weight 1
diogoteles08 added a commit to diogoteles08/scorecard that referenced this issue Sep 20, 2023
…ke changes

As discussed at the issue ossf#2727, we're adding the "require PRs prior
to make changes" as another requirement to tier 2. In addition to that,
we're changing the weight of the tier 2 requirements so that
"requiring 1 reviewer" has weight 2, while the other tier 2 requirements
have weight 1

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>
diogoteles08 added a commit to diogoteles08/scorecard that referenced this issue Sep 27, 2023
…ke changes

As discussed at the issue ossf#2727, we're adding the "require PRs prior
to make changes" as another requirement to tier 2. In addition to that,
we're changing the weight of the tier 2 requirements so that
"requiring 1 reviewer" has weight 2, while the other tier 2 requirements
have weight 1

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>
@github-actions
Copy link

This issue is stale because it has been open for 60 days with no activity.

diogoteles08 added a commit to diogoteles08/scorecard that referenced this issue Nov 13, 2023
…ke changes

As discussed at the issue ossf#2727, we're adding the "require PRs prior
to make changes" as another requirement to tier 2. In addition to that,
we're changing the weight of the tier 2 requirements so that
"requiring 1 reviewer" has weight 2, while the other tier 2 requirements
have weight 1

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>
diogoteles08 added a commit to diogoteles08/scorecard that referenced this issue Nov 17, 2023
…ke changes

As discussed at the issue ossf#2727, we're adding the "require PRs prior
to make changes" as another requirement to tier 2. In addition to that,
we're changing the weight of the tier 2 requirements so that
"requiring 1 reviewer" has weight 2, while the other tier 2 requirements
have weight 1

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>
spencerschrock pushed a commit that referenced this issue Dec 12, 2023
* feat(branch-protection): consider if project requires PRs prior to make changes

As discussed at the issue #2727, we're adding the "require PRs prior
to make changes" as another requirement to tier 2. In addition to that,
we're changing the weight of the tier 2 requirements so that
"requiring 1 reviewer" has weight 2, while the other tier 2 requirements
have weight 1

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* test(branch-protection): increment and adapt testing

1. Adapt previous test cases to consider that now we'll have an aditional
Info log telling that the project requires PRs to make changes.
2. Add more cases to test relevant use cases on the tier 2 level of
branch protection

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* docs(branch-protection-check): adapt check description to consider requirement of require PRs to make changes

It adds the new tier 2 requirement, but also specify that the
"require at least 1 reviewer" will have doubled weight.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor(branch-protection-check): avoid duplicate funcions and enhance readability

Made some nice-to-have improvements on project readability,
making it easier easier to  understand how the branch-protection
score is computed. Also unified 8 different functions that were
doing basically the same thing.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>


* feat(branch-protection): standardize values received on evaluation

Previously, at the evaluation part of branch protetion, the
values nil and false or zero were sort of interchangeble. This commit
changes the code to set as nil only the data that could not be retrieved
from github -- all the others would have values as false, zero, true, etc

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* test(github-client): adapt and add tests to check if nil values are coherent

1. Add new test to evaluate how we're interpreting a rule with all
checkboxes unchecked (most shouldn't be nil)
2. Adapt existent tests to expect non-nil values for unchecked
   checkboxes

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* feat(client-github): avoid reusing bool pointers

Changes some pieces of code to prefer using pointers of
bool instantiated independently. If reusing bool pointers, at some piece
of code the value of the bool could inadvertently changed and it would change the
value of all other fields reusing that pointer.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* feat(branch-protection): enhance evaluation if scorecard was run by admin

At the evaluation step we were using some non untrusted fieldds of the
resposte to evaluate if Scorecard was run as admin or not. Now we're
using a field provided directly from the client file.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* test(branch-protection): adapt testings to say if they have admin info or not

After last commit, the client will tell the evaluation files if
Scorecard was run by administrator or not (i.e., if we have all the
infos). This commit adapts the testings to also provide this info.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* test(e2e-branch-protection): adapt number of logs after changes

- 2 warns (for 'last push approval' and 'codeowners review' disabled) were added because now those informations come as 'not-nil' at the evaluation part.
- 1 info was added to say that PRs are required to make changes
- 1 debug was removed because it said that we couldn't retrieve 'last push approval' information, but we actually can. It was just incorrectly set as nil

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* Revert the 2 commits with changes around how Scorecard detects admin run

Reverts commit 64c3521 and commit e2662b7.
Both had chances around using clients/branch.go scructur to store the
information of whether Scorecard was being run by admin or not. We
decided to not change this structure for this purpose.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor(branch-protection): change data structure to use pointer instead of value

At clients.BranchProtectionRule struct, changing
RequiredPullRequestReviews to be a pointer instead of a struct value.
This will allow the usage of the nil value of this structure to mean
that we can't say if the repository requires reviews or not.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* feat(branch-protection): use nil pointer on reviewers struct to mean
we don't know if they require PRs

The nil value of the struct RequiredPullRequestReviews will now mean
that we can't tell whether the project requires PRs to make changes or not.

When we get this case, we're printing a debug informing that we don't have
this data, but also printing a warn saying that they don't require
reviews, because that will be true at this case.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* test(branch-protection): if we're setting the reviewers struct to nil
when needed

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* doc(branch-protection): add code comment explaining different weight on tier 2 scores

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor(branch-protection): avoid duplicate if branches on reviewers num comparation

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* docs(branch-protection): clarify commentings around data structure

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor: clean code on parsing GitHub BP data

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* feat(branch-protection): ressignify the nil PullRequestReviewRule to mean PR not required

Adapt translation of data from GitHub API, now for our internal data
modeling, having a nil PullRequestReviewRule structure will mean that
PRs are not required on the repo (can also mean we don't have data to
ensure that).

It also changes the order of the calls of copyNonAdminSettings and
copyAdminSettings to make the first one be called first. This eases the
code because the PullRequestReviewRule can be always instantiated at
this function.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* test(branch-protection): ensure we translate GitHub BP data as expected

Ensure we're correctly translating GitHub data from the old Branch
Protection config.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* feat(branch-protection): adapt score evaluation after 2efeee6

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* test(branch-protection): adapt testings to changes of last commits

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* docs(branch-protection): add TODO comments pointing refactor opportunities

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* fix: avoid penalyzing non-admin for dismissStaleReview

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* fix(branch-protection): prevent false value from API field to become nil

When translating the API results, if the specific field `DismissesStaleReviews`
had a false value, it was not being initiated in our data model and was
remaining nil.

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor: clarify different weight on first reviewer

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor: enhance clarity of loggings and comments

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* test(branch-protection): new test to cover different rules affecting same branch

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* docs(branch-protection): change requirements ordering to keep admin ones together

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor(branch-protection): simplify auxiliary function

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor(branch-protection): fix code format to linter requirements

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor(branch-protection): avoid unnecessary initializations and rename function

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* test(branch-protection): adapt test that was forgotten on commit 6858790

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor(branch-protection): use enums to represent tiers

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor(branch-protection): remove nil fields of struct initialization when they dont contribute for clarification

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor(branch-protection): simplify functions by using generics

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* docs(branch-protection): update docs after generate-docs run

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* fix(branch-protection): fix duplicated line on code

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* fix(branch-protection): stop exporting Tier enum

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* refactor(branch-protection): changing unchanged var to const

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

* test(branch-protection): Rename test and adapt it to be consistent with its purpose

I also changed the test to not require PRs, as it's how it is when a new GitHub
Branch Protection config is created. The changes on the loggings numbers are due
to:
1. A warning for not having DismissStaleReviews became a debug
2. Removed the warning we had for not requiring CodeOwners
3. Have a new warning for not requiring PRe

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>

---------

Signed-off-by: Diogo Teles Sant'Anna <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants