diff --git a/.contentful/vault-secrets.yaml b/.contentful/vault-secrets.yaml new file mode 100644 index 00000000..7beafba2 --- /dev/null +++ b/.contentful/vault-secrets.yaml @@ -0,0 +1,5 @@ +version: 1 +services: + github-action: + policies: + - dependabot diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..c2939d92 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +* @contentful/team-security +go.mod +go.sum +.github/workflows diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4a8b14a5..e0747127 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,13 @@ updates: directory: "/" schedule: interval: "daily" +- package-ecosystem: docker + directory: "/" + schedule: + interval: daily + time: "00:00" + timezone: UTC + open-pull-requests-limit: 15 + commit-message: + prefix: build + include: scope diff --git a/.github/workflows/dependabot-approve-and-request-merge.yml b/.github/workflows/dependabot-approve-and-request-merge.yml new file mode 100644 index 00000000..a2c9b5de --- /dev/null +++ b/.github/workflows/dependabot-approve-and-request-merge.yml @@ -0,0 +1,15 @@ +name: "dependabot approve-and-request-merge" + +on: pull_request_target + +jobs: + worker: + permissions: + contents: write + id-token: write + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + steps: + - uses: contentful/github-auto-merge@v1 + with: + VAULT_URL: ${{ secrets.VAULT_URL }} diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml new file mode 100644 index 00000000..e1206208 --- /dev/null +++ b/.github/workflows/docker-image.yaml @@ -0,0 +1,46 @@ +name: Create and publish a Docker image + +on: + release: + types: [published] + # publish on pushes to the main branch (image tagged as "latest") + push: + branches: + - "main" + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + if: github.actor != 'dependabot[bot]' || github.actor != 'dependabot-preview[bot]' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/postmerge.yaml b/.github/workflows/postmerge.yaml index e4105f9a..83ed49d2 100644 --- a/.github/workflows/postmerge.yaml +++ b/.github/workflows/postmerge.yaml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: ossf/scorecard-action@v2.1.3 + - uses: ossf/scorecard-action@v2.3.3 with: results_file: results.sarif results_format: sarif diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 06b3a951..a10d5d2e 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -9,9 +9,9 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: - go-version: '1.21' + go-version: '1.20' check-latest: true - - uses: golangci/golangci-lint-action@v4 + - uses: golangci/golangci-lint-action@v6 with: args: --timeout 3m --verbose build: @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: - go-version: '1.21' + go-version: '1.20' check-latest: true - run: go build -v ./... test: @@ -29,7 +29,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: - go-version: '1.21' + go-version: '1.20' check-latest: true - run: go test -v ./... - run: go vet ./... diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index 160c61d3..00000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,48 +0,0 @@ -name: release - -on: - push: - tags: - - '*' - -permissions: - id-token: write # Undocumented OIDC support. - packages: write # To publish container images to GHCR - contents: write # To create a release - -jobs: - release: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 - with: - go-version: '1.21' - check-latest: true - - - uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0 - - - uses: ko-build/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6 - - - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.workflow }} --password-stdin - - - run: ko publish -B ./cmd/allstar --tags ${{ github.ref_name }} --image-refs allstar.ref - env: - KO_DOCKER_REPO: ghcr.io/${{ github.repository_owner }} - - run: ko publish -B ./cmd/allstar --tags ${{ github.ref_name }}-busybox --image-refs allstar-busybox.ref - env: - KO_DOCKER_REPO: ghcr.io/${{ github.repository_owner }} - KO_DEFAULTBASEIMAGE: cgr.dev/chainguard/busybox - - run: | - echo "signing $(cat allstar.ref)" - cosign sign --yes -a git_sha="$GITHUB_SHA" "$(cat allstar.ref)" - echo "signing $(cat allstar-busybox.ref)" - cosign sign --yes -a git_sha="$GITHUB_SHA" "$(cat allstar-busybox.ref)" - - - run: | - gh release create ${{ github.ref_name }} --notes "Images: - * ghcr.io/${{ github.repository_owner }}/allstar:${{ github.ref_name }} - * ghcr.io/${{ github.repository_owner }}/allstar:${{ github.ref_name }}-busybox" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/sast.yaml b/.github/workflows/sast.yaml new file mode 100644 index 00000000..665de0f6 --- /dev/null +++ b/.github/workflows/sast.yaml @@ -0,0 +1,31 @@ +name: SAST (Static Application Security Testing) + +on: + push: + branches: [master, main] + pull_request: + branches: [master, main] + +jobs: + polaris: + name: polaris / code-scan + continue-on-error: true + runs-on: ubuntu-latest + if: (github.repository_owner == 'contentful') && (endsWith(github.actor, '[bot]') == false) + steps: + - name: Clone repo + uses: actions/checkout@v4 # v4 + with: + fetch-depth: 0 + + - name: Synopsys Polaris + uses: contentful/polaris-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + polaris_url: ${{ secrets.POLARIS_SERVER_URL }} + polaris_access_token: ${{ secrets.POLARIS_ACCESS_TOKEN }} + debug: true + polaris_command: analyze -w --coverity-ignore-capture-failure + security_gate_filters: '{ "severity": ["High", "Medium"] }' + fail_on_error: false + report_url: "https://github.com/contentful/security-tools-config/issues/new?title=False%20positive%20in%20Polaris" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 996c2308..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,41 +0,0 @@ -# Contributing - -All community members must abide by the [OpenSSF Code of -Conduct.](https://openssf.org/community/code-of-conduct/) - -* Feel free to open issues for bugs, feature requests, discussion, - questions, help, proposals, etc. -* If you want to contribute a small fix or feature, open a PR. -* If you want to contribute something larger, a discussion or proposal - issue may be appropriate. -* Please update docs when contributing features. -* When contributing large features, upate [whats-new.md](whats-new.md) -* All git commits must have [DCO](https://wiki.linuxfoundation.org/dco) - -## Contributor Ladder - -Allstar follows the [OpenSSF Scorecard contributor ladder](https://github.com/ossf/scorecard/blob/main/CONTRIBUTOR_LADDER.md). - -Details on the previous Allstar contributor ladder can be found [here](/contributor-ladder.md). - -## Community - -Allstar is a part of the [OpenSSF Scorecard](https://github.com/ossf/scorecard) project. - -We're hanging out in [#allstar](https://openssf.slack.com/archives/C02UQ2RL0HM) on the OpenSSF Slack workspace. - -Meetings and additional community details are [here](https://github.com/ossf/scorecard#connect-with-the-scorecard-community). - -## Development - -* Run tests: `go test -v ./...` -* Run lint: `golangci-lint run` -* Local testing: See [operator.md](operator.md) to setup a test instance for yourself. - -## Contribute Policies - -[Interface definition.](pkg/policydef/policydef.go) - -Both the [SECURITY.md](pkg/policies/security/security.go) and [Outside -Collaborators](pkg/policies/outside/outside.go) policies are quite simple to -understand and good examples to copy. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..0881c182 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# Use the official Go image as the base image +FROM golang:1.22 AS builder + +# Set the working directory +WORKDIR /ko-app + +# Copy the Go application source code +COPY . . + +# Tidy em packages +RUN go mod tidy + +# Build the Go application +RUN CGO_ENABLED=0 go build -o allstar ./cmd/allstar + +# Use a minimal base image to reduce the image size +FROM gcr.io/distroless/base-debian10 + +# Copy the binary from the builder stage to the final image +COPY --from=builder /ko-app/allstar /ko-app/allstar + +# Set the entry point for the final image +ENTRYPOINT ["/ko-app/allstar"] diff --git a/README.md b/README.md index 15c97fee..a084b996 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/ossf/allstar/badge)](https://api.securityscorecards.dev/projects/github.com/ossf/allstar) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/contentful/allstar/badge)](https://api.securityscorecards.dev/projects/github.com/contentful/allstar) @@ -8,14 +8,6 @@ - [What Is Allstar?](#what-is-allstar) -## What's new with Allstar - -- [whats-new.md](whats-new.md) - -## Disabling Unwanted Issues - -- [Help! I'm getting issues created by Allstar and I don't want them!](#disabling-unwanted-issues-1) - ## Getting Started - [Background](#background) @@ -27,14 +19,16 @@ ## Policies and Actions - [Actions](#actions) - [Policies](#policies) +- [Enabled Policies](#enabled-policies) ## Advanced - [Configuration Definitions](#configuration-definitions) - [Example Configurations](#example-config-repository) - [Run Your Own Instance of Allstar](operator.md) - -## Contribute -- [Contributing](#contributing) +- [Contentful Architecture](architecture.md) +- [Creating a New Policy](create-a-new-policy.md) +- [Deploying Changes](deployment.md) +- [Opt-out of Checks](opt-out.md) ________ ________ @@ -56,11 +50,6 @@ handle policy violations. You can also develop or contribute new policies. Allstar is developed as a part of the [OpenSSF Scorecard](https://github.com/ossf/scorecard) project. -## [What's new with Allstar](whats-new.md) - -## Disabling Unwanted Issues -If you're getting unwanted issues created by Allstar, follow [these directions](opt-out.md) to opt out. - ## Getting Started ### Background @@ -163,34 +152,6 @@ configured at the org level. Both the Quickstart and Manual Installation options involve installing the Allstar app. You may review the permissions requested. The app asks for read access to most settings and file contents to detect security compliance. It requests write access to issues and checks so that it can create issues and allow the `block` action. -#### Quickstart Installation -This installation option will enable Allstar using the -Opt Out strategy on all repositories in your organization. All current policies -will be enabled, and Allstar will alert you of -policy violations by filing an issue. This is the quickest and easiest way to start using Allstar, and you can still change any configurations later. - -Effort: very easy - -Steps: - -1. Install the Allstar app - 1. [Open the installation - page](https://github.com/apps/allstar-app) and click Configure - 1. If you have multiple organizations, select the one you want to - install Allstar on - 1. Select "All Repositories" under Repository Access, even if you - plan to disable Allstar on some repositories later -1. Fork the sample repository - 1. [Open the sample repository](https://github.com/jeffmendoza/dot-allstar-quickstart) - and click the "Use this template" button - 1. In the field for Repository Name, type `.allstar` - 1. Click "Create repository from template" - -That's it! All current Allstar [policies](#policies) are now enabled on all -your repositories. Allstar will create an issue if a policy is violated. - -To change any configurations, see the [manual installation directions](manual-install.md). - #### Manual Installation This installation option will walk you through creating configuration files according to either the Opt In or Opt Out strategy. This @@ -262,12 +223,52 @@ action: issue The details of how the `fix` action works for each policy is detailed below. If omitted below, the `fix` action is not applicable. -### Branch Protection +## **Enabled Policies** +The status of controls currently applied to the Contentful Github Org + +|Policy Name|Tracked|Enforced| +|-----------|-------|--------| +|[CODEOWNERS](#codeowners)|Yes|No| +|[Roadie Info](#roadie-info)|In Progress|No| +|[Branch Protection](#branch-protection)|Yes|No| +|Binary Artifact|No|No| +|Outside Collaborators|No|No| +|SECURITY.md|No|No| +|Dangerous Workflow|No|No| +|Generic Scorecard|No|No| +|Github Actions|No|No| +|Repository Admins|No|No| + -This policy's config file is named `branch_protection.yaml`, and the [config +### CODEOWNERS + +This policy's config file is named `codeowners.yaml`, and the [config definitions are -here](https://pkg.go.dev/github.com/ossf/allstar/pkg/policies/branch#OrgConfig). +here](https://pkg.go.dev/github.com/ossf/allstar/pkg/policies/codeowners#OrgConfig). + +The branch protection policy checks that a valid [CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) file exists in the repo +are setup correctly according to the specified configuration. The issue text +will describe which setting is incorrect. See [GitHub's +documentation](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) +for correcting settings. +The `fix` action will open a PR creating a CODEOWNERS that sets the populates the owner based on the `owner` field in the repos catalog-info.yaml. + +### Roadie Info + +This policy's config file is named `roadie.yaml`, and the [config +definitions are +here](TODO). + +The branch protection policy checks that a valid [catalog-info.yaml](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) file exists in the repo are setup correctly according to the specified configuration. See [Backstages' +documentation](https://backstage.io/docs/features/software-catalog/descriptor-format/#contents) +for correcting settings. + +The `fix` action will open a PR creating a catalog-info.yaml with some default values. The annotations are service/repo specific and will need to be updated to be accurate. + +### Branch Protection + +This policy's config file is named `branch_protection.yaml` The branch protection policy checks that GitHub's [branch protection settings](https://docs.github.com/en/github/administering-a-repository/defining-the-mergeability-of-pull-requests/about-protected-branches) are setup correctly according to the specified configuration. The issue text @@ -279,10 +280,7 @@ The `fix` action will change the branch protection settings to be in compliance ### Binary Artifacts -This policy's config file is named `binary_artifacts.yaml`, and the [config -definitions are -here](https://pkg.go.dev/github.com/ossf/allstar/pkg/policies/binary#OrgConfig). - +This policy's config file is named `binary_artifacts.yaml` This policy incorporates the [check from scorecard](https://github.com/ossf/scorecard/#scorecard-checks). Remove the binary artifact from the repository to achieve compliance. As the scorecard @@ -291,10 +289,7 @@ itself](https://github.com/ossf/scorecard) to see all the detailed information. ### Outside Collaborators -This policy's config file is named `outside.yaml`, and the [config definitions -are -here](https://pkg.go.dev/github.com/ossf/allstar/pkg/policies/outside#OrgConfig). - +This policy's config file is named `outside.yaml` This policy checks if any [Outside Collaborators](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/adding-outside-collaborators-to-repositories-in-your-organization) have either administrator(default) or push(optional) access to the @@ -303,10 +298,7 @@ untrusted members can change admin level settings and commit malicious code. ### SECURITY.md -This policy's config file is named `security.yaml`, and the [config definitions -are -here](https://pkg.go.dev/github.com/ossf/allstar/pkg/policies/security#OrgConfig). - +This policy's config file is named `security.yaml` This policy checks that the repository has a security policy file in `SECURITY.md` and that it is not empty. The created issue will have a link to the [GitHub @@ -315,10 +307,7 @@ that helps you commit a security policy to your repository. ### Dangerous Workflow -This policy's config file is named `dangerous_workflow.yaml`, and the [config -definitions are -here](https://pkg.go.dev/github.com/ossf/allstar/pkg/policies/workflow#OrgConfig). - +This policy's config file is named `dangerous_workflow.yaml` This policy checks the GitHub Actions workflow configuration files (`.github/workflows`), for any patterns that match known dangerous behavior. See the [Security Scorecards @@ -327,10 +316,7 @@ for more information on this check. ### Generic Scorecard Check -This policy's config file is named `scorecard.yaml`, and the [config definitions -are -here](https://pkg.go.dev/github.com/ossf/allstar/pkg/policies/scorecard#OrgConfig). - +This policy's config file is named `scorecard.yaml` This policy runs any scorecard check listed in the `checks` configuration. All checks run must have a score equal or above the `threshold` setting. Please see the [Security Scorecards @@ -339,10 +325,7 @@ for more information on each check. ### GitHub Actions -This policy's config file is named `actions.yaml`, and the [config definitions -are -here](https://pkg.go.dev/github.com/ossf/allstar/pkg/policies/action#OrgConfig). - +This policy's config file is named `actions.yaml` This policy checks the GitHub Actions workflow configuration files (`.github/workflows`) (and workflow runs in some cases) in each repo to ensure they are in line with rules (eg. require, deny) defined in the @@ -350,10 +333,7 @@ organization-level config for the policy. ### Repository Administrators -This policy's config file is named `admin.yaml`, and the [config definitions -are -here](https://pkg.go.dev/github.com/ossf/allstar/pkg/policies/admin#OrgConfig). - +This policy's config file is named `admin.yaml` This policy checks that by default all repositories must have a user or group assigned as an Administrator. It allows you to optionally configure if users are allowed to be administrators (as opposed to teams). ### Future Policies @@ -372,8 +352,8 @@ organization. ### Configuration Definitions -- [Organization level enable configuration](https://pkg.go.dev/github.com/ossf/allstar/pkg/config#OrgOptConfig) -- [Repository Override enable configuration]( https://pkg.go.dev/github.com/ossf/allstar/pkg/config#RepoOptConfig) +- [Organization level enable configuration]() +- [Repository Override enable configuration]() ### Secondary Org-Level configuration location @@ -442,8 +422,4 @@ This will use all the config from `acme/.allstar` as the base config, but then apply any changes in the current file on top of the base configuration. The method this is applied is described as a [JSON Merge Patch](https://datatracker.ietf.org/doc/html/rfc7396). The `baseConfig` must be -a GitHub `/`. - -## **Contributing** - -See [CONTRIBUTING.md](CONTRIBUTING.md) +a GitHub `/`. \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 65b40899..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,11 +0,0 @@ -# Reporting Security Issues - -To report a security issue, please email -[oss-security@googlegroups.com](mailto:oss-security@googlegroups.com) -with a description of the issue, the steps you took to create the issue, -affected versions, and, if known, mitigations for the issue. - -Our vulnerability management team will respond within 3 working days of your -email. If the issue is confirmed as a vulnerability, we will open a -Security Advisory and acknowledge your contributions as part of it. This project -follows a 90 day disclosure timeline. diff --git a/app-prod.yaml b/app-prod.yaml deleted file mode 100644 index d0e13111..00000000 --- a/app-prod.yaml +++ /dev/null @@ -1,13 +0,0 @@ -runtime: custom -env: flex -manual_scaling: - instances: 1 -resources: - cpu: 2 - memory_gb: 12 - disk_size_gb: 100 -env_variables: - APP_ID: 119816 - KEY_SECRET: "gcpsecretmanager://projects/allstar-ossf/secrets/allstar-private-key?decoder=bytes" - DO_NOTHING_ON_OPT_OUT: true - ALLSTAR_NUM_WORKERS: 1 diff --git a/app-staging.yaml b/app-staging.yaml deleted file mode 100644 index a9623f7b..00000000 --- a/app-staging.yaml +++ /dev/null @@ -1,13 +0,0 @@ -runtime: custom -env: flex -service: staging -manual_scaling: - instances: 1 -resources: - cpu: 2 - memory_gb: 12 - disk_size_gb: 100 -env_variables: - APP_ID: 166485 - KEY_SECRET: "gcpsecretmanager://projects/allstar-ossf/secrets/allstar-staging-private-key?decoder=bytes" - ALLSTAR_NUM_WORKERS: 1 diff --git a/architecture.md b/architecture.md new file mode 100644 index 00000000..bfe3b65b --- /dev/null +++ b/architecture.md @@ -0,0 +1 @@ +[Miro board with current architecture](https://miro.com/app/board/uXjVM2g_HGQ=/?share_link_id=272309503681) \ No newline at end of file diff --git a/catalog-info-template.yaml b/catalog-info-template.yaml new file mode 100644 index 00000000..d4c97533 --- /dev/null +++ b/catalog-info-template.yaml @@ -0,0 +1,28 @@ +# Backstage documentation +# https://backstage.io/docs/features/software-catalog/descriptor-format/ + +apiVersion: backstage.io/v1alpha1 +kind: unknown +metadata: + name: $REPO_NAME #pulled from gh api + description: $REPO_DESCRIPTION #pulled from gh api + annotations: + github.com/project-slug: $PROJECT_SLUG #pulled from gh api + contentful.com/service-tier: "unknown" #1, 2, 3, 4 + + tags: + - update-me + #need to add sast.yaml to .github/workflows and enable it in polaris dashboard + #once that is done this can be changed to sast-enabled + - sast-disabled + #make this match the value from service-tier above + - tier-unknown +spec: + #cli, component, contentful.com/template, documentation, function, library, service, template, website + type: unknown + #deprecated, experimental, production, unknown + lifecycle: unknown + system: unknown + #we can assign owner based on who created the repo or submitted the first PR but theres + #no guarantee 1. they still work here, 2. are on the owning team anymore + owner: group:team-unknown \ No newline at end of file diff --git a/catalog-info.yaml b/catalog-info.yaml new file mode 100644 index 00000000..b7167746 --- /dev/null +++ b/catalog-info.yaml @@ -0,0 +1,20 @@ +# Backstage documentation +# https://backstage.io/docs/features/software-catalog/descriptor-format/ + +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: allstar + description: Allstar is a GitHub App that continuously monitors GitHub organizations or repositories for adherence to security best practices + annotations: + github.com/project-slug: contentful/allstar + contentful.com/service-tier: "4" + + tags: + - update-me + - sast-disabled + - tier-4 +spec: + type: service + lifecycle: production + owner: group:team-security diff --git a/cloudbuild.yaml b/cloudbuild.yaml deleted file mode 100644 index 5d7e4c1c..00000000 --- a/cloudbuild.yaml +++ /dev/null @@ -1,15 +0,0 @@ -steps: -- name: golang:1.21 - entrypoint: go - args: ['install', 'github.com/google/ko@v0.15.2'] -- name: golang:1.21 - entrypoint: bash - args: ['-c', 'KO_DOCKER_REPO="gcr.io/allstar-ossf" /go/bin/ko publish ./cmd/allstar > container'] -- name: gcr.io/google.com/cloudsdktool/cloud-sdk - entrypoint: bash - args: ['-c', 'gcloud app deploy --appyaml=app-staging.yaml --project=allstar-ossf --image-url $(cat container)'] -timeout: 1200s -options: - volumes: - - name: go-modules - path: /go diff --git a/cmd/allstar/main.go b/cmd/allstar/main.go index b57c7b6a..37717d58 100644 --- a/cmd/allstar/main.go +++ b/cmd/allstar/main.go @@ -25,9 +25,9 @@ import ( "syscall" "time" - "github.com/ossf/allstar/pkg/enforce" - "github.com/ossf/allstar/pkg/ghclients" - "github.com/ossf/allstar/pkg/policies" + "github.com/contentful/allstar/pkg/enforce" + "github.com/contentful/allstar/pkg/ghclients" + "github.com/contentful/allstar/pkg/policies" "github.com/rs/zerolog" "github.com/rs/zerolog/log" diff --git a/cmd/reviewbot/main.go b/cmd/reviewbot/main.go index ad1421e0..b52cec72 100644 --- a/cmd/reviewbot/main.go +++ b/cmd/reviewbot/main.go @@ -19,7 +19,7 @@ import ( "os" "strconv" - "github.com/ossf/allstar/pkg/reviewbot" + "github.com/contentful/allstar/pkg/reviewbot" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) diff --git a/code-of-conduct.md b/code-of-conduct.md deleted file mode 100644 index d83fe17a..00000000 --- a/code-of-conduct.md +++ /dev/null @@ -1,2 +0,0 @@ -All community members must abide by the [OpenSSF Code of -Conduct.](https://openssf.org/community/code-of-conduct/) diff --git a/contributor-ladder.md b/contributor-ladder.md deleted file mode 100644 index a3e0b428..00000000 --- a/contributor-ladder.md +++ /dev/null @@ -1,29 +0,0 @@ -# Contributor Ladder - -***Allstar follows the [OpenSSF Scorecard contributor ladder](https://github.com/ossf/scorecard/blob/main/CONTRIBUTOR_LADDER.md).*** - -Contributors to Allstar are documented in [MAINTAINERS.md](/MAINTAINERS.md). - -Appeals will be discussed as an agenda item in the [OpenSSF Scorecard project meetings](https://github.com/ossf/scorecard#connect-with-the-scorecard-community). - -## Changes - -This section briefly details key differences between the OpenSSF Scorecard contributor ladder and the legacy Allstar contributor ladder. - -- *Active* legacy "Contributors" who are not OpenSSF GitHub organization members are now ["Community Members"](https://github.com/ossf/scorecard/blob/main/CONTRIBUTOR_LADDER.md#community-members) - - Community Members are eligible for [OpenSSF GitHub organization](https://github.com/ossf) membership. Outside collaborator access [***should never be granted***](https://github.com/ossf/tac/blob/main/policies/access.md#teams-not-individuals). -- Existing OpenSSF GitHub organization members who are actively reviewing content can be granted [Triagers access](https://github.com/ossf/scorecard/blob/main/CONTRIBUTOR_LADDER.md#triagers) -- Legacy "Maintainers" and "Leaders" are now considered equivalent [access levels][maintainers-access] -- Existing OpenSSF Scorecard maintainers may be granted [Maintainer access][maintainers-access], based on need - -[maintainers-access]: https://github.com/ossf/scorecard/blob/main/CONTRIBUTOR_LADDER.md#maintainers - -## Legacy contributor ladder - -This section exists for historical purposes to detail the legacy Allstar contributor ladder, ***which is no longer in use***. - -| Name | Prerequisites: Indicators to look for before promotion | Expectations | Recognition, Access, Effect of access | -| - | - | - | - | -| Contributors | One of: 1. Regular PRs for code/docs. 2. Regular feedback on discussion topics in issues/slack 3. Regular meeting attendance and contribution. For a period of time: 30 days or more. | Continue pre-req. | Added as GitHub Outside Collaborator to repo with read permissions. Tests on PRs run automatically without approval | -| Maintainers | Regular PRs for code/docs of moderate to substantial impact. Participates in many high-level proposals and discussions. | Available to have PRs assigned to them for review: Will review within 3 business days, otherwise notifies team when unable to review (ooo, etc.) Responds to requests for input/discussion over issues and slack | Added as GitHub Outside Collaborator to repo with write/push access. Approves and merges PRs | -| Leaders | Consistently drives the project through code, proposals, project direction, leadership, etc. | As a group, responds and makes final decisions through consensus on all large proposals, features, architecture changes, design, documentation of project | Full access and ownership. Creates releases, and finalizes changelog updates, sends project update announcements. | diff --git a/create-a-new-policy.md b/create-a-new-policy.md new file mode 100644 index 00000000..10e5dcc1 --- /dev/null +++ b/create-a-new-policy.md @@ -0,0 +1,2 @@ +1. Create a new folder in pkg/policies +2. Create your check and unit test in that folder \ No newline at end of file diff --git a/deploy b/deploy deleted file mode 100755 index 157afd06..00000000 --- a/deploy +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -export KO_DOCKER_REPO="gcr.io/allstar-ossf" -cont=$(ko publish ./cmd/allstar) -gcloud app deploy --image-url ${cont} diff --git a/deployment.md b/deployment.md new file mode 100644 index 00000000..645abeb1 --- /dev/null +++ b/deployment.md @@ -0,0 +1,2 @@ +* When changes are merged to `main` a [Github Action](.github/workflows/docker-image.yaml) is kicked off that builds a new image and uploads it to ECR. +* This image is used by [allstar-agent](https://github.com/contentful/allstar-agent) and will kick off a new deployment(automatically??) of the agent to the `contentful-security-sandbox` organization. \ No newline at end of file diff --git a/opt-out.md b/opt-out.md index 428a9976..519683a6 100644 --- a/opt-out.md +++ b/opt-out.md @@ -1,72 +1,10 @@ # How to disable Allstar If you are receiving unwanted issues created by Allstar, follow the instructions on this page to disable the app on your project. -Allstar is highly configurable, so to disable it you need to know: - -- Whether Allstar was installed at the organization level or directly on your - repository -- Whether Allstar was configured using the opt-in or opt-out strategy - (if it was installed at the organization level) - -If you already know how Allstar is configured on your organization or repository, -follow the instructions for the appropriate configuration: - [Disable Allstar, org-level opt-out strategy](#disable-allstar-org-level-opt-out-strategy) -[Disable Allstar, org-level opt-in strategy](#disable-allstar-org-level-opt-in-strategy) -[Disable Allstar, repository level](#disable-allstar-repository-level) - -If you did not install Allstar yourself and do not know which instructions to -follow, you should contact your administrator to find out how Allstar is -configured on your organization. - -If you are unable to contact the administrator, you can still disable of -Allstar, but it will take a little more work. Follow [these instructions -](#determine-how-allstar-is-configured)to figure out how Allstar is configured on your project. - -## Determine how Allstar is configured -
- Click to expand - -Follow these instructions if you are unable to contact your administrator to -find out how Allstar is configured on your organization or repository. - -In your organization, find the repository named `.allstar`. - -In the `.allstar` repository, find the file named `allstar.yaml.` - -In that file, look for a setting that says: - -``` - optConfig: - - optOutStrategy: -``` - -- If `optOutStrategy` is set to `true`, follow the [opt-out strategy - instructions](#disable-allstar-org-level-opt-out-strategy). - -- If `optOutStrategy` is set to `false`, follow the [opt-in strategy - instructions](#disable-allstar-org-level-opt-in-strategy). - -If this setting, file, or repository does not exist, it means that your project has been opted-in elsewhere and you will need to determine where: - -Check the org-level `allstar.yaml` file for your repo. It may look like this: - -``` -optConfig: - optInRepos: - - other-repo - - other-repo-two - - my-repo-name-here - - yet-another-repo -``` +[Disable Check, repository level](#disable-a-specific-check-with-repo-override) +[Disable Allstar, repo level](#disable-allstar-with-repo-override) -If your repository is on the `optInRepos` list, follow the [opt-in strategy -instructions](#disable-allstar-org-level-opt-in-strategy). - -If your repository is not listed in the allstar.yaml file, it means Allstar is -configured directly on your repository. Follow the [repository-level instructions](#disable-allstar-repository-level). -
## Disable Allstar, org-level opt-out strategy @@ -94,25 +32,9 @@ optConfig: Allstar will be disabled on your repository when the pull request is merged. -### Alternative option: with repo-override - -This alternative option uses the `repo-override` setting to avoid the need to -submit a pull request to the organization's `.allstar` repo, but works only if: - -- the org-level `allstar.yaml` config has the line `disableRepoOverride: - false` - -or - -- the org-level `allstar.yaml` config file does not the include - `disableRepoOverride` setting (which defaults to `false`). - -If `disableRepoOverride` is set to `true`, the following instructions will not -work. - -To disable Allstar using repo-override, create a file in your repo named -`.allstar/allstar.yaml` with the contents: +### Disable a specific check with repo-override +To opt-out of a specific check in your repo create `.allstar/control-name.yaml` and add ``` optConfig: optOut: true @@ -120,37 +42,14 @@ optConfig: Merge this file to disable Allstar on your repository. -## Disable Allstar, org-level opt-in strategy - -These instructions disable Allstar on a repository when Allstar is configured at the organization level using the opt-in strategy. +### Disable allstar with repo-override -In the org-level .allstar repository, open the `allstar.yaml` file. Find the -`optInRepos` setting: - -``` -optConfig: - optInRepos: - - other-repo - - other-repo-two - - my-repo-name-here - - yet-another-repo -``` - -Submit a pull request to the `.allstar` repo that removes your repo name from that list. - -When the pull request is merged, Allstar should be disabled on your repository. If you still continue to receive issues, though, it means your project was also opted-in at the repository level. You must also follow the [repository-level instructions](disable-allstar-repository-level). - -## Disable Allstar, repository level - -These instructions disable Allstar when it is configured directly on your repository (not at the organization level). - -Look in your repository for a file named `.allstar/allstar.yaml`. It - should contain this setting: +To disable Allstar using repo-override, create a file in your repo named +`.allstar/allstar.yaml` with the contents: ``` optConfig: - optIn: true + optOut: true ``` -Remove the `.allstar/allstar.yaml` file from your repository to - disable Allstar. +Merge this file to disable Allstar on your repository. diff --git a/pkg/config/config.go b/pkg/config/config.go index 7658ea60..8e7d8b5d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -22,7 +22,7 @@ import ( "path" "strings" - "github.com/ossf/allstar/pkg/config/operator" + "github.com/contentful/allstar/pkg/config/operator" jsonpatch "github.com/evanphx/json-patch/v5" "github.com/google/go-github/v59/github" diff --git a/pkg/config/location.go b/pkg/config/location.go index 97a17178..a3b5a795 100644 --- a/pkg/config/location.go +++ b/pkg/config/location.go @@ -19,7 +19,7 @@ import ( "net/http" "sync" - "github.com/ossf/allstar/pkg/config/operator" + "github.com/contentful/allstar/pkg/config/operator" ) type instLoc struct { diff --git a/pkg/config/operator/operator.go b/pkg/config/operator/operator.go index 636138a9..42526c5b 100644 --- a/pkg/config/operator/operator.go +++ b/pkg/config/operator/operator.go @@ -82,7 +82,7 @@ const GitHubIssueLabel = "allstar" // GitHubIssueFooter is added to the end of GitHub issues. const GitHubIssueFooter = `This issue will auto resolve when the policy is in compliance. -Issue created by Allstar. See https://github.com/ossf/allstar/ for more information. For questions specific to the repository, please contact the owner or maintainer.` +Issue created by Allstar. See https://github.com/contentful/allstar/ for more information. For questions specific to the repository, please contact the owner or maintainer.` // AllowedOrganizations is the set of GitHub repositories on which this Allstar instance // is allowed to be installed. This allows a public GitHub app to be shared between GitHub diff --git a/pkg/config/schedule/schedule.go b/pkg/config/schedule/schedule.go index feefd6f6..df75569e 100644 --- a/pkg/config/schedule/schedule.go +++ b/pkg/config/schedule/schedule.go @@ -20,7 +20,7 @@ import ( "strings" "time" - "github.com/ossf/allstar/pkg/config" + "github.com/contentful/allstar/pkg/config" "github.com/rs/zerolog/log" ) diff --git a/pkg/config/schedule/schedule_test.go b/pkg/config/schedule/schedule_test.go index 7c61ef1e..29158b4c 100644 --- a/pkg/config/schedule/schedule_test.go +++ b/pkg/config/schedule/schedule_test.go @@ -18,7 +18,7 @@ import ( "testing" "time" - "github.com/ossf/allstar/pkg/config" + "github.com/contentful/allstar/pkg/config" ) func timeFromDay(wd time.Weekday) time.Time { diff --git a/pkg/enforce/enforce.go b/pkg/enforce/enforce.go index 684ec28f..76dc9693 100644 --- a/pkg/enforce/enforce.go +++ b/pkg/enforce/enforce.go @@ -22,13 +22,13 @@ import ( "sync" "time" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/config/operator" - "github.com/ossf/allstar/pkg/ghclients" - "github.com/ossf/allstar/pkg/issue" - "github.com/ossf/allstar/pkg/policies" - "github.com/ossf/allstar/pkg/policydef" - "github.com/ossf/allstar/pkg/scorecard" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/config/operator" + "github.com/contentful/allstar/pkg/ghclients" + "github.com/contentful/allstar/pkg/issue" + "github.com/contentful/allstar/pkg/policies" + "github.com/contentful/allstar/pkg/policydef" + "github.com/contentful/allstar/pkg/scorecard" "golang.org/x/sync/errgroup" "github.com/google/go-github/v59/github" @@ -294,7 +294,13 @@ func getAppInstallationReposReal(ctx context.Context, ic *github.Client) ([]*git if err != nil { break } - repos = append(repos, rs.Repositories...) + for _, repo := range rs.Repositories { + if !repo.GetArchived() { + repos = append(repos, repo) + continue + } + continue + } if resp.NextPage == 0 { break } diff --git a/pkg/issue/issue.go b/pkg/issue/issue.go index 5e9e5ae2..2d8dacc9 100644 --- a/pkg/issue/issue.go +++ b/pkg/issue/issue.go @@ -24,9 +24,9 @@ import ( "strings" "time" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/config/operator" - "github.com/ossf/allstar/pkg/config/schedule" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/config/operator" + "github.com/contentful/allstar/pkg/config/schedule" "github.com/rs/zerolog/log" "github.com/google/go-github/v59/github" @@ -287,7 +287,7 @@ func createIssueBody(owner, repo, text, hash, footer string, isIssueRepo bool) s refersTo = fmt.Sprintf(" and refers to [%s](https://github.com/%s)", ownerRepo, ownerRepo) } editHeader := issueSectionHeader(updateSectionName) - return fmt.Sprintf("_This issue was automatically created by [Allstar](https://github.com/ossf/allstar/)%s._\n\n**Security Policy Violation**\n"+ + return fmt.Sprintf("_This issue was automatically created by [Allstar](https://github.com/contentful/allstar/)%s._\n\n**Security Policy Violation**\n"+ "%v\n\n---\n\n%s%s%s\n%v", refersTo, text, editHeader, fmt.Sprintf(resultTextHashCommentFormat, hash), editHeader, footer) } diff --git a/pkg/issue/issue_test.go b/pkg/issue/issue_test.go index 21e8d6cd..3de6e0b4 100644 --- a/pkg/issue/issue_test.go +++ b/pkg/issue/issue_test.go @@ -21,8 +21,8 @@ import ( "testing" "time" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/config/operator" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/config/operator" "github.com/google/go-github/v59/github" ) @@ -70,8 +70,8 @@ func TestEnsure(t *testing.T) { issueTitleOtherRepo := "Security Policy violation for repository \"\" thispolicy" closed := "closed" open := "open" - body := "_This issue was automatically created by [Allstar](https://github.com/ossf/allstar/)._\n\n**Security Policy Violation**\nStatus text\n\n---\n\n\nThis issue will auto resolve when the policy is in compliance.\n\nIssue created by Allstar. See https://github.com/ossf/allstar/ for more information. For questions specific to the repository, please contact the owner or maintainer." - bodyOtherRepo := "_This issue was automatically created by [Allstar](https://github.com/ossf/allstar/) and refers to [/](https://github.com//)._\n\n**Security Policy Violation**\nStatus text\n\n---\n\n\nThis issue will auto resolve when the policy is in compliance.\n\nIssue created by Allstar. See https://github.com/ossf/allstar/ for more information. For questions specific to the repository, please contact the owner or maintainer." + body := "_This issue was automatically created by [Allstar](https://github.com/contentful/allstar/)._\n\n**Security Policy Violation**\nStatus text\n\n---\n\n\nThis issue will auto resolve when the policy is in compliance.\n\nIssue created by Allstar. See https://github.com/contentful/allstar/ for more information. For questions specific to the repository, please contact the owner or maintainer." + bodyOtherRepo := "_This issue was automatically created by [Allstar](https://github.com/contentful/allstar/) and refers to [/](https://github.com//)._\n\n**Security Policy Violation**\nStatus text\n\n---\n\n\nThis issue will auto resolve when the policy is in compliance.\n\nIssue created by Allstar. See https://github.com/contentful/allstar/ for more information. For questions specific to the repository, please contact the owner or maintainer." configGetAppConfigs = func(context.Context, *github.Client, string, string) (*config.OrgConfig, *config.RepoConfig, *config.RepoConfig) { return &config.OrgConfig{}, &config.RepoConfig{}, &config.RepoConfig{} } @@ -142,7 +142,7 @@ func TestEnsure(t *testing.T) { configGetAppConfigs = func(context.Context, *github.Client, string, string) (*config.OrgConfig, *config.RepoConfig, *config.RepoConfig) { return &config.OrgConfig{IssueFooter: "CustomFooter"}, &config.RepoConfig{}, &config.RepoConfig{} } - bodyWithFooter := "_This issue was automatically created by [Allstar](https://github.com/ossf/allstar/)._\n\n**Security Policy Violation**\nStatus text\n\n---\n\n\nCustomFooter\n\nThis issue will auto resolve when the policy is in compliance.\n\nIssue created by Allstar. See https://github.com/ossf/allstar/ for more information. For questions specific to the repository, please contact the owner or maintainer." + bodyWithFooter := "_This issue was automatically created by [Allstar](https://github.com/contentful/allstar/)._\n\n**Security Policy Violation**\nStatus text\n\n---\n\n\nCustomFooter\n\nThis issue will auto resolve when the policy is in compliance.\n\nIssue created by Allstar. See https://github.com/contentful/allstar/ for more information. For questions specific to the repository, please contact the owner or maintainer." listByRepo = func(ctx context.Context, owner string, repo string, opts *github.IssueListByRepoOptions) ([]*github.Issue, *github.Response, error) { return make([]*github.Issue, 0), &github.Response{NextPage: 0}, nil diff --git a/pkg/policies/action/action.go b/pkg/policies/action/action.go index da8aba08..8aa0762d 100644 --- a/pkg/policies/action/action.go +++ b/pkg/policies/action/action.go @@ -22,8 +22,8 @@ import ( "strings" "github.com/Masterminds/semver/v3" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/policydef" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" "github.com/rhysd/actionlint" "github.com/google/go-github/v59/github" diff --git a/pkg/policies/admin/admin.go b/pkg/policies/admin/admin.go index e2270a54..46af9476 100644 --- a/pkg/policies/admin/admin.go +++ b/pkg/policies/admin/admin.go @@ -18,9 +18,9 @@ package admin import ( "context" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" "github.com/gobwas/glob" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/policydef" "github.com/google/go-github/v59/github" "github.com/rs/zerolog/log" diff --git a/pkg/policies/binary/binary.go b/pkg/policies/binary/binary.go index ba16d45b..8599c9be 100644 --- a/pkg/policies/binary/binary.go +++ b/pkg/policies/binary/binary.go @@ -21,9 +21,9 @@ import ( "fmt" "path/filepath" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/policydef" - "github.com/ossf/allstar/pkg/scorecard" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" + "github.com/contentful/allstar/pkg/scorecard" "github.com/ossf/scorecard/v4/checker" "github.com/ossf/scorecard/v4/checks" diff --git a/pkg/policies/branch/branch.go b/pkg/policies/branch/branch.go index db2e338f..e9e71385 100644 --- a/pkg/policies/branch/branch.go +++ b/pkg/policies/branch/branch.go @@ -20,8 +20,8 @@ import ( "fmt" "net/http" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/policydef" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" "github.com/google/go-github/v59/github" "github.com/rs/zerolog/log" diff --git a/pkg/policies/catalog/catalog.go b/pkg/policies/catalog/catalog.go new file mode 100644 index 00000000..8da9c4d3 --- /dev/null +++ b/pkg/policies/catalog/catalog.go @@ -0,0 +1,249 @@ +// Copyright 2023 Allstar 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. + +// Package roadie implements the Roadie catalog-info.yaml check policy. +package catalog + +import ( + "context" + "fmt" + + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" + "github.com/shurcooL/githubv4" + + "github.com/google/go-github/v50/github" + "github.com/rs/zerolog/log" +) + +const configFile = "catalog.yaml" +const polName = "Catalog" + +const notifyText = `A catalog-info.yaml file can give users information about which team is responsible for the maintenance of the repository. + +To fix this, add a catalog-info.yaml file to your repository, following the official documentation. +` + +// OrgConfig is the org-level config definition for Catalog policy. +type OrgConfig struct { + // OptConfig is the standard org-level opt in/out config, RepoOverride applies to all + // BP config. + OptConfig config.OrgOptConfig `json:"optConfig"` + + // Action defines which action to take, default log, other: issue... + Action string `json:"action"` + + // RequireCatalog : set to true to require presence of a catalog-info.yaml on the repositories (creates an issue if not present) + // default false (only checks if existing catalog-info.yaml is valid, creates issues if not valid). + RequireCatalog bool `json:"requireCatalog"` +} + +// RepoConfig is the repo-level config for catalog-info.yaml +type RepoConfig struct { + // OptConfig is the standard repo-level opt in/out config. + OptConfig config.RepoOptConfig `json:"optConfig"` + + // Action overrides the same setting in org-level, only if present. + Action *string `json:"action"` + + // RequireCatalog : set to true to require presence of a catalog-info.yaml on the repositories (creates an issue if not present) + // default false (only checks if existing catalog-info.yaml is valid, creates issues if not valid). + RequireCatalog *bool `json:"requireCatalog"` +} + +type v4client interface { + Query(context.Context, interface{}, map[string]interface{}) error +} + +type mergedConfig struct { + Action string + RequireCatalog bool +} + +type details struct { + Enabled bool +} + +var configFetchConfig func(context.Context, *github.Client, string, string, string, config.ConfigLevel, interface{}) error + +var configIsEnabled func(ctx context.Context, o config.OrgOptConfig, orc, r config.RepoOptConfig, c *github.Client, owner, repo string) (bool, error) + +//var catalogExists func(ctx context.Context, c *github.Client, owner, repo string) (bool, error) + +func init() { + configFetchConfig = config.FetchConfig + configIsEnabled = config.IsEnabled +} + +// Catalog is the catalog-info.yaml policy object, implements policydef.Policy. +type Catalog bool + +// NewCatalog returns a new catalog-info.yaml policy. +func NewCatalog() policydef.Policy { + var s Catalog + return s +} + +// Name returns the name of this policy, implementing policydef.Policy.Name() +func (s Catalog) Name() string { + return polName +} + +// Check performs the policy check for catalog-info.yaml policy based on the +// configuration stored in the org/repo, implementing policydef.Policy.Check() +func (s Catalog) Check(ctx context.Context, c *github.Client, owner, + repo string) (*policydef.Result, error) { + v4c := githubv4.NewClient(c.Client()) + return check(ctx, c, v4c, owner, repo) +} + +// Check whether this policy is enabled or not +func (s Catalog) IsEnabled(ctx context.Context, c *github.Client, owner, repo string) (bool, error) { + oc, orc, rc := getConfig(ctx, c, owner, repo) + return configIsEnabled(ctx, oc.OptConfig, orc.OptConfig, rc.OptConfig, c, owner, repo) +} + +func check(ctx context.Context, c *github.Client, v4c v4client, owner, + repo string) (*policydef.Result, error) { + oc, orc, rc := getConfig(ctx, c, owner, repo) + enabled, err := configIsEnabled(ctx, oc.OptConfig, orc.OptConfig, rc.OptConfig, c, owner, repo) + if err != nil { + return nil, err + } + log.Info(). + Str("org", owner). + Str("repo", repo). + Str("area", polName). + Bool("enabled", enabled). + Msg("Check repo enabled") + + var q struct { + Repository struct { + Object struct { + Blob struct { + Text string + } `graphql:"... on Blob"` + } `graphql:"object(expression: $expression)"` + } `graphql:"repository(owner: $owner, name: $name)"` + } + variables := map[string]interface{}{ + "owner": githubv4.String(owner), + "name": githubv4.String(repo), + "expression": githubv4.String(fmt.Sprintf("%s:%s", "HEAD", "catalog-info.yaml")), + } + if err := v4c.Query(ctx, &q, variables); err != nil { + return nil, err + } + if len(q.Repository.Object.Blob.Text) == 0 { + return &policydef.Result{ + Enabled: enabled, + Pass: false, + NotifyText: "catalog-info.yaml file not found.\n" + fmt.Sprint(notifyText, owner, repo), + Details: details{ + Enabled: false, + }, + }, nil + } + return &policydef.Result{ + Enabled: enabled, + Pass: true, + NotifyText: "", + Details: details{ + Enabled: true, + }, + }, nil +} + +// Fix implementing policydef.Policy.Fix(). Currently not supported. Plan +// to support this TODO. +func (s Catalog) Fix(ctx context.Context, c *github.Client, owner, repo string) error { + log.Warn(). + Str("org", owner). + Str("repo", repo). + Str("area", polName). + Msg("Action fix is configured, but not implemented.") + return nil +} + +// GetAction returns the configured action from catalog-info.yaml policy's +// configuration stored in the org-level repo, default log. Implementing +// policydef.Policy.GetAction() +func (s Catalog) GetAction(ctx context.Context, c *github.Client, owner, repo string) string { + oc, orc, rc := getConfig(ctx, c, owner, repo) + mc := mergeConfig(oc, orc, rc, repo) + return mc.Action +} + +func getConfig(ctx context.Context, c *github.Client, owner, repo string) (*OrgConfig, *RepoConfig, *RepoConfig) { + oc := &OrgConfig{ // Fill out non-zero defaults + Action: "log", + RequireCatalog: false, + } + if err := configFetchConfig(ctx, c, owner, "", configFile, config.OrgLevel, oc); err != nil { + log.Error(). + Str("org", owner). + Str("repo", repo). + Str("configLevel", "orgLevel"). + Str("area", polName). + Str("file", configFile). + Err(err). + Msg("Unexpected config error, using defaults.") + } + orc := &RepoConfig{} + if err := configFetchConfig(ctx, c, owner, repo, configFile, config.OrgRepoLevel, orc); err != nil { + log.Error(). + Str("org", owner). + Str("repo", repo). + Str("configLevel", "orgRepoLevel"). + Str("area", polName). + Str("file", configFile). + Err(err). + Msg("Unexpected config error, using defaults.") + } + rc := &RepoConfig{} + if err := configFetchConfig(ctx, c, owner, repo, configFile, config.RepoLevel, rc); err != nil { + log.Error(). + Str("org", owner). + Str("repo", repo). + Str("configLevel", "repoLevel"). + Str("area", polName). + Str("file", configFile). + Err(err). + Msg("Unexpected config error, using defaults.") + } + return oc, orc, rc +} + +func mergeConfig(oc *OrgConfig, orc *RepoConfig, rc *RepoConfig, repo string) *mergedConfig { + mc := &mergedConfig{ + Action: oc.Action, + RequireCatalog: oc.RequireCatalog, + } + mc = mergeInRepoConfig(mc, orc, repo) + + if !oc.OptConfig.DisableRepoOverride { + mc = mergeInRepoConfig(mc, rc, repo) + } + return mc +} + +func mergeInRepoConfig(mc *mergedConfig, rc *RepoConfig, repo string) *mergedConfig { + if rc.Action != nil { + mc.Action = *rc.Action + } + if rc.RequireCatalog != nil { + mc.RequireCatalog = *rc.RequireCatalog + } + return mc +} diff --git a/pkg/policies/catalog/catalog_test.go b/pkg/policies/catalog/catalog_test.go new file mode 100644 index 00000000..3b5508ba --- /dev/null +++ b/pkg/policies/catalog/catalog_test.go @@ -0,0 +1,261 @@ +// Copyright 2021 Allstar 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. + +package catalog + +import ( + "context" + "fmt" + "testing" + + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" + "github.com/google/go-cmp/cmp" + "github.com/google/go-github/v50/github" +) + +var query func(context.Context, interface{}, map[string]interface{}) error + +type mockClient struct{} + +func (m mockClient) Query(ctx context.Context, q interface{}, v map[string]interface{}) error { + return query(ctx, q, v) +} + +func TestConfigPrecedence(t *testing.T) { + tests := []struct { + Name string + Org OrgConfig + OrgRepo RepoConfig + Repo RepoConfig + ExpAction string + Exp mergedConfig + }{ + { + Name: "OrgOnly", + Org: OrgConfig{ + Action: "issue", + }, + OrgRepo: RepoConfig{}, + Repo: RepoConfig{}, + ExpAction: "issue", + Exp: mergedConfig{ + Action: "issue", + }, + }, + { + Name: "OrgRepoOverOrg", + Org: OrgConfig{ + Action: "issue", + }, + OrgRepo: RepoConfig{ + Action: github.String("log"), + }, + Repo: RepoConfig{}, + ExpAction: "log", + Exp: mergedConfig{ + Action: "log", + }, + }, + { + Name: "RepoOverAllOrg", + Org: OrgConfig{ + Action: "issue", + }, + OrgRepo: RepoConfig{ + Action: github.String("log"), + }, + Repo: RepoConfig{ + Action: github.String("email"), + }, + ExpAction: "email", + Exp: mergedConfig{ + Action: "email", + }, + }, + { + Name: "RepoDisallowed", + Org: OrgConfig{ + OptConfig: config.OrgOptConfig{ + DisableRepoOverride: true, + }, + Action: "issue", + }, + OrgRepo: RepoConfig{ + Action: github.String("log"), + }, + Repo: RepoConfig{ + Action: github.String("email"), + }, + ExpAction: "log", + Exp: mergedConfig{ + Action: "log", + }, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + configFetchConfig = func(ctx context.Context, c *github.Client, + owner, repo, path string, ol config.ConfigLevel, out interface{}) error { + switch ol { + case config.RepoLevel: + rc := out.(*RepoConfig) + *rc = test.Repo + case config.OrgRepoLevel: + orc := out.(*RepoConfig) + *orc = test.OrgRepo + case config.OrgLevel: + oc := out.(*OrgConfig) + *oc = test.Org + } + return nil + } + + s := Catalog(true) + ctx := context.Background() + + action := s.GetAction(ctx, nil, "", "thisrepo") + if action != test.ExpAction { + t.Errorf("Unexpected results. want %s, got %s", test.ExpAction, action) + } + + oc, orc, rc := getConfig(ctx, nil, "", "thisrepo") + mc := mergeConfig(oc, orc, rc, "thisrepo") + if diff := cmp.Diff(&test.Exp, mc); diff != "" { + t.Errorf("Unexpected results. (-want +got):\n%s", diff) + } + }) + } +} + +func TestCheck(t *testing.T) { + const notifyText = `A catalog-info.yaml file can give users information about which team is responsible for the maintenance of the repository. + +To fix this, add a catalog-info.yaml file to your repository, following the official documentation. +` + tests := []struct { + Name string + Org OrgConfig + Repo RepoConfig + CatalogAdded string + cofigEnabled bool + Exp policydef.Result + }{ + { + Name: "NotEnabled", + Org: OrgConfig{}, + Repo: RepoConfig{}, + CatalogAdded: "catalog-info.yaml", + cofigEnabled: false, + Exp: policydef.Result{ + Enabled: false, + Pass: true, + NotifyText: "", + Details: details{ + Enabled: true, + }, + }, + }, + { + Name: "Pass", + Org: OrgConfig{ + OptConfig: config.OrgOptConfig{ + OptOutStrategy: true, + }, + }, + Repo: RepoConfig{}, + CatalogAdded: "catalog-info.yaml", + cofigEnabled: true, + Exp: policydef.Result{ + Enabled: true, + Pass: true, + NotifyText: "", + Details: details{ + Enabled: true, + }, + }, + }, + { + Name: "Fail", + Org: OrgConfig{ + OptConfig: config.OrgOptConfig{ + OptOutStrategy: true, + }, + }, + Repo: RepoConfig{}, + CatalogAdded: "", + cofigEnabled: true, + Exp: policydef.Result{ + Enabled: true, + Pass: false, + NotifyText: "catalog-info.yaml file not found.\n" + fmt.Sprint(notifyText, OrgConfig{}, RepoConfig{}), + Details: details{ + Enabled: false, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + configFetchConfig = func(ctx context.Context, c *github.Client, + owner, repo, path string, ol config.ConfigLevel, out interface{}) error { + if repo == "thisrepo" && ol == config.RepoLevel { + rc := out.(*RepoConfig) + *rc = test.Repo + } else if ol == config.OrgLevel { + oc := out.(*OrgConfig) + *oc = test.Org + } + return nil + } + query = func(ctx context.Context, q interface{}, v map[string]interface{}) error { + qc, ok := q.(*struct { + Repository struct { + Object struct { + Blob struct { + Text string + } `graphql:"... on Blob"` + } `graphql:"object(expression: $expression)"` + } `graphql:"repository(owner: $owner, name: $name)"` + }) + if !ok { + t.Errorf("Query() called with unexpected query structure.") + } + qc.Repository.Object.Blob.Text = test.CatalogAdded + return nil + } + configIsEnabled = func(ctx context.Context, o config.OrgOptConfig, orc, r config.RepoOptConfig, + c *github.Client, owner, repo string) (bool, error) { + return test.cofigEnabled, nil + } + res, err := check(context.Background(), nil, mockClient{}, "", "thisrepo") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + c := cmp.Comparer(func(x, y string) bool { return trunc(x, 40) == trunc(y, 40) }) + if diff := cmp.Diff(&test.Exp, res, c); diff != "" { + t.Errorf("Unexpected results. (-want +got):\n%s", diff) + } + }) + } +} + +func trunc(s string, n int) string { + if n >= len(s) { + return s + } + return s[:n] +} diff --git a/pkg/policies/codeowners/codeowners.go b/pkg/policies/codeowners/codeowners.go index e2bfb044..bc6239b1 100644 --- a/pkg/policies/codeowners/codeowners.go +++ b/pkg/policies/codeowners/codeowners.go @@ -20,8 +20,8 @@ import ( "fmt" "net/http" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/policydef" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" "github.com/google/go-github/v59/github" "github.com/rs/zerolog/log" diff --git a/pkg/policies/outside/outside.go b/pkg/policies/outside/outside.go index e4819318..1ea91afa 100644 --- a/pkg/policies/outside/outside.go +++ b/pkg/policies/outside/outside.go @@ -19,9 +19,9 @@ import ( "context" "fmt" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" "github.com/gobwas/glob" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/policydef" "github.com/google/go-github/v59/github" "github.com/rs/zerolog/log" diff --git a/pkg/policies/policies.go b/pkg/policies/policies.go index 284f8da0..c2b07ac9 100644 --- a/pkg/policies/policies.go +++ b/pkg/policies/policies.go @@ -17,16 +17,17 @@ package policies import ( - "github.com/ossf/allstar/pkg/policies/action" - "github.com/ossf/allstar/pkg/policies/admin" - "github.com/ossf/allstar/pkg/policies/binary" - "github.com/ossf/allstar/pkg/policies/branch" - "github.com/ossf/allstar/pkg/policies/codeowners" - "github.com/ossf/allstar/pkg/policies/outside" - "github.com/ossf/allstar/pkg/policies/scorecard" - "github.com/ossf/allstar/pkg/policies/security" - "github.com/ossf/allstar/pkg/policies/workflow" - "github.com/ossf/allstar/pkg/policydef" + "github.com/contentful/allstar/pkg/policies/action" + "github.com/contentful/allstar/pkg/policies/admin" + "github.com/contentful/allstar/pkg/policies/binary" + "github.com/contentful/allstar/pkg/policies/branch" + "github.com/contentful/allstar/pkg/policies/catalog" + "github.com/contentful/allstar/pkg/policies/codeowners" + "github.com/contentful/allstar/pkg/policies/outside" + "github.com/contentful/allstar/pkg/policies/scorecard" + "github.com/contentful/allstar/pkg/policies/security" + "github.com/contentful/allstar/pkg/policies/workflow" + "github.com/contentful/allstar/pkg/policydef" ) // GetPolicies returns a slice of all policies in Allstar. @@ -41,5 +42,6 @@ func GetPolicies() []policydef.Policy { workflow.NewWorkflow(), action.NewAction(), admin.NewAdmin(), + catalog.NewCatalog(), } } diff --git a/pkg/policies/scorecard/scorecard.go b/pkg/policies/scorecard/scorecard.go index 66f68bd6..43d15ad9 100644 --- a/pkg/policies/scorecard/scorecard.go +++ b/pkg/policies/scorecard/scorecard.go @@ -20,9 +20,9 @@ import ( "fmt" "net/http" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/policydef" - "github.com/ossf/allstar/pkg/scorecard" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" + "github.com/contentful/allstar/pkg/scorecard" "github.com/ossf/scorecard/v4/checker" "github.com/ossf/scorecard/v4/checks" diff --git a/pkg/policies/security/security.go b/pkg/policies/security/security.go index 29297a8c..e483a3df 100644 --- a/pkg/policies/security/security.go +++ b/pkg/policies/security/security.go @@ -19,8 +19,8 @@ import ( "context" "fmt" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/policydef" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" "github.com/google/go-github/v59/github" "github.com/rs/zerolog/log" diff --git a/pkg/policies/workflow/workflow.go b/pkg/policies/workflow/workflow.go index f0e87370..0621d418 100644 --- a/pkg/policies/workflow/workflow.go +++ b/pkg/policies/workflow/workflow.go @@ -20,9 +20,9 @@ import ( "context" "fmt" - "github.com/ossf/allstar/pkg/config" - "github.com/ossf/allstar/pkg/policydef" - "github.com/ossf/allstar/pkg/scorecard" + "github.com/contentful/allstar/pkg/config" + "github.com/contentful/allstar/pkg/policydef" + "github.com/contentful/allstar/pkg/scorecard" "github.com/ossf/scorecard/v4/checker" "github.com/ossf/scorecard/v4/checks" diff --git a/pkg/policydef/policydef.go b/pkg/policydef/policydef.go index 7d2f11d8..a76cd730 100644 --- a/pkg/policydef/policydef.go +++ b/pkg/policydef/policydef.go @@ -18,7 +18,7 @@ // Policies should define and retrieve their own config in the same way that // Allstar does. There should be an org-level config and repo-level // config. Each config should include the OptConfig defined in -// github.com/ossf/allstar/pkg/config to determine if the policy is enabled or +// github.com/contentful/allstar/pkg/config to determine if the policy is enabled or // disabled. The config package also provided helper functions to retrieve // config from the repo. package policydef diff --git a/whats-new.md b/whats-new.md deleted file mode 100644 index b65a1fca..00000000 --- a/whats-new.md +++ /dev/null @@ -1,94 +0,0 @@ -# What's new with Allstar - -Major features and changes added to Allstar. - -## Added since last release - -- - -## Release v3.0 - -- Branch Protection policy is more complete with support for - `requireSignedCommits`,` enforceOnAdmins`, - `requireCodeOwnerReviews`. [Link](pkg/policies/branch/branch.go) - -- You may now opt-out repos that are forks with the `optOutForkedRepos` option. - -- GitHub Actions policy added to allow/require/deny configured actions in - workflows. [Docs](README.md#github-actions) - -- Generic Scorecard policy added to run any Scorecard check with a score - threshold. [Docs](README.md#generic-scorecard-check) - -- Issue creation and pinging can be enabled / disabled based on a weekly - schedule. [Link](pkg/config/config.go) - -- The Outside Collaborators policy now allows - exemptions. [Link](pkg/policies/outside/outside.go) - -- When the Allstar action is changed from `issue` to `fix`. Existing issues - will be closed. - -- Issue ping duration is configurable at the operator level with - `NOTICE_PING_DURATION_HOURS`. [Link](pkg/config/operator/operator.go) - -- Org config may now point to a secondary repository for config and merge - overrides. [Docs](README.md#org-level-base-and-merge-configuration-location) - -- Individual repo config files are now allowed to be placed in the central org - config repository. Example: in the `.allstar` repo, you can have a - `/branch_protection.yaml` file with specific settings for that - repo. [Docs](README.md#repo-policy-configurations-in-the-org-repo) - -- Binary Artifacts policy configuration updated to have an ignore - list. [Link](pkg/policies/binary/binary.go) - -- Dangerous Workflow policy added. This policy checks the GitHub Actions - workflow configuration files (.github/workflows), for any patterns that match - known dangerous behavior. [Docs](README.md#dangerous-workflow) - -## Release v2.0 - -- Branch Protection added the `requireStatusChecks` setting to ensure listed - status checks are set in protection settings. Also enforces the - `requireUpToDateBranch` option, if `requireStatusChecks` is set. - -- You may now opt-out of repos marked as "archived" in GitHub with the - `optOutArchivedRepos` option. - -- Binary Artifacts policy issue text improved. - -- A custom footer can be added to all issues created in an organization with - the `issueFooter` option. - -- Branch Protection now supports the "fix" action. - -## Proposed functionality changes in v2.0 - -- Option `testingOwnerlessAllowed` in Outside Collaborator policy. Currently - defaults true, proposal to default to false in next release. - - - Note: this was temporarily enabled in Jan, but then turned off due to a bug. - -## Pre v2.0 - -Regular releases were not made before v2.0, so all previous notes are here. - -- All issues for an org can be routed to a single repo using the `issueRepo` - setting. - -- Org config can now be located in `.github/allstar` as a secondary location - after the `.allstar` repo. - -- Issues can be created with a custom label using the `issueLabel` option. - -- Private or Public repositories can be opt-out as a group with the - `optOutPrivateRepos` or `optOutPublicRepos` options. - -- We will retroactively call this Allstar v1.0: Allstar announced - https://openssf.org/blog/2021/08/11/introducing-the-allstar-github-app/ - -- Initial policies and features built - -- Allstar was proposed to the OpenSSF Securing Critical Projects WG and - accepted https://youtu.be/o3SiBDUTCrw?t=300