Skip to content

Policy for checking for arbitrary file existence #500

Open
@wesley-dean-flexion

Description

@wesley-dean-flexion

Policy for checking for arbitrary file existence

User Story

As a Security Engineer, so that I can enforce organization-wide policies regarding arbitrary files, I would like a new File Check policy that I may configure to meet my organization's needs.

Overview

OSSF Allstar current includes policies for checking the existence of various specific files. For example, consider SECURITY.md via pkg/policies/security/ ; this policy specifies a constant at security.md line 31 of polName with a value of SECURITY.md. In #469, @matias-jt asked if it was possible to verify if a file exists and @ArisBee responded with advice to update the polName variable. Similarly, @melmos asked in #450 if would be possible for a policy to check for security.json instead of SECURITY.md and it was suggested that the two issues may be duplicates with #469 referencing a general use-case while #450 is more specific.

The suggested change in the value of polName would require recompiling the tool -- that is, the value is set at compile-time, not at configuration-time or run-time.

Current Use-Case

My organization has multiple repositories. We have an organizational policy (governance, not Allstar policy) of requiring repositories to include a configuration files for Pre-Commit, a tool that supports hooks for running detect-secrets, gitleaks, etc.. That is, we want to iterate across all our repos and make sure they all have .pre-commit-config.yaml files -- basically exactly what Allstar does, but instead of SECURITY.md, we would look for .pre-commit-config.yaml files. While we could fork Allstar, we're thinking that it may be possible to help others who are in similar situations (e.g., the aforementioned issues) by generalizing a policy in Allstar that could be configured separately.

Alternative Implementation

High-level Goals

  1. Allow changing the name of the file to be check at configuration-time
  2. Allow the policy to check for the existence of absence of a file
  3. Allow the policy to check in multiple locations
  4. Allow the policy to check for multiple files

Without getting overly-prescriptive, it may be worthwhile to consider breaking down a new policy into smaller goals and iterate across them instead of trying to do the whole thing at once.

Iterative Steps

Iteration 1: variable filenames

Allow polName to be set via configuration file. The configuration file would function just like the other tools' configuration files (e.g., security.md line 30 which specifies configFile = "security.yaml"). The sample quickstart file would be extended to support an additional parameter, such as fileName. At runtime, instead of referencing a constant, a variable would be extracted from the configuration file. Everything else would remain the same. That is, if the file in question exists, policy compliance is accepted; if it's missing, the repository is non-compliant and the usual remedies (block checks, create issues, etc.) are taken.

It is recommended that basic checks are run to only allow tests for files in the repository, not arbitrary files on the filesystem.

  1. read fileName string from the config file
  2. if fileName exists, pass
  3. otherwise fail

Consider the following configuration file:

---
optConfig:
  optOutStrategy: true
  action: issue

  fileName: "SECURITY.md"

Iteration 2: support file absence

Instead of having a policy check receive configuration via a string named fileName, the tool will accept a map with two keys of fileName and state where fileName is the name of the target file (as before) and state is either present or absent (consider Ansible's ansible.builtin.file module which allows absent and a variety of potential file types).

File exists? State Result
Yes Present Pass
Yes Absent Fail
No Present Fail
No Absent Pass

The most significant aspect of this iteration isn't the supporting the absence of a file, it's changing the configuration approach
from accepting a string to accepting a map.

  1. read fileCheck map from the config file
  2. extract fileName string from fileCheck
  3. extract state string from fileCheck
  4. if fileName exists and state == present, pass
  5. if filename doesn't exist and state == absent, pass
  6. otherwise, fail

Consider the following configuration file:

---
optConfig:
  optOutStrategy: true
  action: issue

  fileCheck:
    fileName: "SECURITY.md"
    state: "present"

Iteration 3: support multiple locations

Instead of having the policy check for a single specific location for a file (e.g., /SECURITY.md) specified as a string, accept an array of locations to check. In the case of checking for file existence, if any of the locations match, consider the check passed; in checking for file absence, if any of the locations match, conside the check failed. For example, the SECURITY.md file may be located at:

  • /SECURITY.md
  • /.github/SECURITY.md
  • /docs/SECURITY.md

The most significant change here is having the location be specified as an array rather than a string and iterating across the array instead of just checking one location.

  1. read fileCheck map from the config file
  2. extract fileNames array from fileCheck
  3. extract state string from fileCheck
  4. iterate across fileNames
    a. if state == present and any match, pass; otherwise fail
    b. if state == absent and any match, fail; otherwise pass

Consider the following configuration file:

---
optConfig:
  optOutStrategy: true
  action: issue

  fileCheck
    fileNames:
      - "/SECURITY.md"
      - "/.github/SECURITY.md"
      - "/docs/SECURITY.md"
    state: "present"

Iteration 4: support multiple checks

Currently, there's a 1:1 correspondence with regards to file checks and policies (e.g., the Security policy can't look for a CODEOWNERS file); this iteration supports multiple checks.

Instead of accepting a single map, fileCheck, accept an array of maps with each item representing the check for
a single file (although it may be in multiple locations).

Consider the following configuration file:

---
optConfig:
  optOutStrategy: true
  action: issue

  files:
    - SECURITY.md:
      fileNames:
        - "/SECURITY.md"
        - "/.github/SECURITY.md"
        - "/docs/SECURITY.md"
      state: "present"

    - CODEOWNERS:
      fileNames:
        - "/CODEOWNERS"
        - "/.github/CODEOWNERS"
        - "/docs/CODEOWNERS"
      state: "present"
  1. read files array from the config file
  2. iterate across files array
  3. extract fileNames array from iterant
  4. extract state string from iterant
  5. iterate across fileNames
    a. if state == present and any match, pass; otherwise fail
    b. if state == absent and any match, fail; otherwise pass

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions