Skip to content

Commit

Permalink
✨ Added independent probe for required MFA
Browse files Browse the repository at this point in the history
Signed-off-by: Eddie Knight <[email protected]>
  • Loading branch information
eddie-knight committed Oct 30, 2024
1 parent 1703089 commit 76b2b23
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 1 deletion.
4 changes: 4 additions & 0 deletions clients/git/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,10 @@ func (c *Client) GetDefaultBranch() (*clients.BranchRef, error) {
return nil, clients.ErrUnsupportedFeature
}

func (c *Client) GetMFARequired() (required bool, err error) {
return required, clients.ErrUnsupportedFeature
}

func (c *Client) GetOrgRepoClient(ctx context.Context) (clients.RepoClient, error) {
return nil, clients.ErrUnsupportedFeature
}
Expand Down
11 changes: 11 additions & 0 deletions clients/githubrepo/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,17 @@ func (client *Client) GetCreatedAt() (time.Time, error) {
return client.repo.CreatedAt.Time, nil
}

func (client *Client) GetMFARequired() (required bool, err error) {
org, _, err := client.repoClient.Organizations.Get(context.Background(), client.repourl.owner)
if err != nil {
return
}
if org.TwoFactorRequirementEnabled != nil {
return *org.TwoFactorRequirementEnabled, nil
}
return false, nil
}

func (client *Client) GetOrgRepoClient(ctx context.Context) (clients.RepoClient, error) {
dotGithubRepo, err := MakeGithubRepo(fmt.Sprintf("%s/.github", client.repourl.owner))
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions clients/gitlabrepo/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ func (client *Client) GetCreatedAt() (time.Time, error) {
return client.project.getCreatedAt()
}

func (c *Client) GetMFARequired() (required bool, err error) {
err = fmt.Errorf("GetMFARequired: %w", clients.ErrUnsupportedFeature)
return
}

func (client *Client) GetOrgRepoClient(ctx context.Context) (clients.RepoClient, error) {
return nil, fmt.Errorf("GetOrgRepoClient (GitLab): %w", clients.ErrUnsupportedFeature)
}
Expand Down
5 changes: 5 additions & 0 deletions clients/localdir/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ func (client *localDirClient) GetDefaultBranchName() (string, error) {
return "", fmt.Errorf("GetDefaultBranchName: %w", clients.ErrUnsupportedFeature)
}

func (c *localDirClient) GetMFARequired() (required bool, err error) {
err = fmt.Errorf("GetMFARequired: %w", clients.ErrUnsupportedFeature)
return
}

// ListCommits implements RepoClient.ListCommits.
func (client *localDirClient) ListCommits() ([]clients.Commit, error) {
return nil, fmt.Errorf("ListCommits: %w", clients.ErrUnsupportedFeature)
Expand Down
8 changes: 8 additions & 0 deletions clients/mockclients/repo_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions clients/ossfuzz/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ func (c *client) GetDefaultBranch() (*clients.BranchRef, error) {
return nil, fmt.Errorf("GetDefaultBranch: %w", clients.ErrUnsupportedFeature)
}

func (c *client) GetMFARequired() (required bool, err error) {
err = fmt.Errorf("GetMFARequired: %w", clients.ErrUnsupportedFeature)
return
}

// GetOrgRepoClient implements RepoClient.GetOrgRepoClient.
func (c *client) GetOrgRepoClient(ctx context.Context) (clients.RepoClient, error) {
return nil, fmt.Errorf("GetOrgRepoClient: %w", clients.ErrUnsupportedFeature)
Expand Down
1 change: 1 addition & 0 deletions clients/repo_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type RepoClient interface {
GetCreatedAt() (time.Time, error)
GetDefaultBranchName() (string, error)
GetDefaultBranch() (*BranchRef, error)
GetMFARequired() (bool, error)
GetOrgRepoClient(context.Context) (RepoClient, error)
ListCommits() ([]Commit, error)
ListIssues() ([]Issue, error)
Expand Down
4 changes: 3 additions & 1 deletion probes/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,9 @@ var (
}

// Probes which don't use pre-computed raw data but rather collect it themselves.
Independent = []IndependentProbeImpl{}
Independent = []IndependentProbeImpl{
orgRequiresMFA.Run,

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / generate-docs

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / generate-docs

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / generate-docs

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / check-linter

undefined: orgRequiresMFA (typecheck)

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / check-linter

undefined: orgRequiresMFA) (typecheck)

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / check-linter

undefined: orgRequiresMFA) (typecheck)

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / validate-docs

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / validate-docs

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / validate-docs

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / unit-test

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / gitlab-integration-trusted

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / gitlab-integration-trusted

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / gitlab-integration-trusted

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / integration-trusted

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / integration-trusted

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / integration-trusted

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / build-scorecard

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / build-scorecard

undefined: orgRequiresMFA

Check failure on line 177 in probes/entries.go

View workflow job for this annotation

GitHub Actions / build-scorecard

undefined: orgRequiresMFA
}
)

//nolint:gochecknoinits
Expand Down
30 changes: 30 additions & 0 deletions probes/orgRequiresMFA/def.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2024 OpenSSF Scorecard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

id: orgRequiresMFA # required
lifecycle: experimental # required
short: A short description of this probe
motivation: >
What is the motivation for this probe?
implementation: >
How does this probe work under-the-hood?
outcome:
- If MFA is found to be required, the probe returns OutcomeTrue
- IF MFA is not found to be required, the probe returns OutcomeFalse
- If the runtime environment does not have authentication for the target project, the probe returns OutcomeNotAvailable
remediation:
onOutcome: False # required
effort: Low # required
text:
- In your project settings, require MFA for all collaborators.
75 changes: 75 additions & 0 deletions probes/orgRequiresMFA/impl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2024 OpenSSF Scorecard Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//nolint:stylecheck
package orgRequiresMFA

import (
"embed"
"fmt"

"github.com/ossf/scorecard/v5/checker"
"github.com/ossf/scorecard/v5/finding"
"github.com/ossf/scorecard/v5/internal/probes"
"github.com/ossf/scorecard/v5/probes/internal/utils/uerror"
)

//go:embed *.yml
var fs embed.FS

const (
Probe = "orgRequiresMFA"
)

func init() {
// Register independently of any checks
probes.MustRegisterIndependent(Probe, Run)
}

func Run(raw *checker.CheckRequest) (found []finding.Finding, probeName string, err error) {
if raw == nil {
err = fmt.Errorf("raw results is nil: %w", uerror.ErrNil)
return found, Probe, err
}

mfaRequired, err := raw.RepoClient.GetMFARequired()
if err != nil {
err = fmt.Errorf("getting MFA required: %w", err)
return found, Probe, err
}

var outcome finding.Outcome
if mfaRequired {
outcome = finding.OutcomeTrue
} else {
outcome = finding.OutcomeFalse
}

result, err := finding.NewWith(
fs,
Probe,
"Collaborators require MFA",
nil,
outcome,
)

if err != nil {
err = fmt.Errorf("creating finding: %w", err)
return found, Probe, err
}

found = append(found, *result)

return found, Probe, err
}
103 changes: 103 additions & 0 deletions probes/orgRequiresMFA/impl_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 76b2b23

Please sign in to comment.