Creating a profile
Prerequisites
- The
minder
CLI application - A Minder account with
at least
editor
permission
Use a reference rule
The first step to creating a profile is to create the rules that your profile will apply.
The Minder team has provided several reference rules for common use cases. To get started quickly, create a rule from the set of references.
Fetch all the reference rules by cloning the minder-rules-and-profiles repository.
git clone https://github.com/mindersec/minder-rules-and-profiles.git
In that directory you can find all the reference rules and profiles.
cd minder-rules-and-profiles
Create the secret_scanning
rule type in Minder:
minder ruletype create -f rule-types/github/secret_scanning.yaml
Write your own rule
This section describes how to write your own rule, using the existing rule secret_scanning
as a reference. If you've
already created the secret_scanning
rule, you may choose to skip this section.
Start by creating a rule that checks if secret scanning is enabled.
Create a new file called secret_scanning.yaml
.
Add some basic information about the rule to the new file, such as the version, type, name, context, description and guidance.
---
version: v1
type: rule-type
name: secret_scanning
context:
provider: github
description: Verifies that secret scanning is enabled for a given repository.
# guidance is the instructions the user will see if this rule fails
guidance: |
Secret scanning is a feature that scans repositories for secrets and alerts
the repository owner when a secret is found. To enable this feature in GitHub,
you must enable it in the repository settings.
For more information, see
https://docs.github.com/en/github/administering-a-repository/about-secret-scanning
Next, add the rule definition to the secret_scanning.yaml
file.
Set in_entity
to be repository
, since secret scanning is enabled on the repository.
def:
in_entity: repository
Create a rule_schema
defining a property describing whether secret scanning is enabled on a repository.
def:
# ...
rule_schema:
properties:
enabled:
type: boolean
default: true
Set ingest
to make a REST call to fetch information about each registered repository and parse the response as JSON.
def:
# ...
ingest:
type: rest
rest:
# This is the path to the data source. Given that this will evaluate
# for each repository in the organization, we use a template that
# will be evaluated for each repository. The structure to use is the
# protobuf structure for the entity that is being evaluated.
endpoint: "/repos/{{.Entity.Owner}}/{{.Entity.Name}}"
parse: json
Configure eval
to use jq
to read the response from the REST call and determine if secret scanning is enabled.
def:
# ...
eval:
type: jq
jq:
# Ingested points to the data retrieved in the `ingest` section
- ingested:
def: '.security_and_analysis.secret_scanning.status == "enabled"'
# profile points to the profile itself.
profile:
def: ".enabled"
Set up the remediation action that will be taken if this rule is not satisfied (and the profile has turned on remediation). The remediation action in this case is to make a PATCH request to the repository and enable secret scanning.
def:
# ...
remediate:
type: rest
rest:
method: PATCH
endpoint: "/repos/{{.Entity.Owner}}/{{.Entity.Name}}"
body: |
{ "security_and_analysis": {"secret_scanning": { "status": "enabled" } } }
Define how users will be alerted if this rule is not satisfied. In this case a security advisory will be created in any repository that does not satisfy this rule.
def:
# ...
alert:
type: security_advisory
security_advisory:
severity: "medium"
Putting it all together, you get the following content in secret_scanning.yaml
:
---
version: v1
type: rule-type
name: secret_scanning
context:
provider: github
description: Verifies that secret scanning is enabled for a given repository.
guidance: |
Secret scanning is a feature that scans repositories for secrets and alerts
the repository owner when a secret is found. To enable this feature in GitHub,
you must enable it in the repository settings.
For more information, see
https://docs.github.com/en/github/administering-a-repository/about-secret-scanning
def:
in_entity: repository
rule_schema:
properties:
enabled:
type: boolean
default: true
ingest:
type: rest
rest:
endpoint: "/repos/{{.Entity.Owner}}/{{.Entity.Name}}"
parse: json
eval:
type: jq
jq:
- ingested:
def: '.security_and_analysis.secret_scanning.status == "enabled"'
profile:
def: ".enabled"
remediate:
type: rest
rest:
method: PATCH
endpoint: "/repos/{{.Entity.Owner}}/{{.Entity.Name}}"
body: |
{ "security_and_analysis": {"secret_scanning": { "status": "enabled" } } }
alert:
type: security_advisory
security_advisory:
severity: "medium"
Finally, create the secret_scanning
rule in Minder:
minder ruletype create -f secret_scanning.yaml
Create a profile
Now that you've created a secret scanning rule, you can set up a profile that checks if secret scanning is enabled in all your registered repositories.
Start by creating a file named profile.yaml
.
Add some basic information about the profile to the new file, such as the version, type, name and context.
version: v1
type: profile
name: my-first-profile
context:
provider: github
Turn on alerting, so that a security advisory will be created for any registered repository that has not enabled secret scanning.
alert: "on"
Turn on remediation, so that secret scanning will automatically be enabled for any registered repositories.
remediate: "on"
Register the secret scanning rule that you created in the previous step.
repository:
- type: secret_scanning
def:
enabled: true
Putting it all together, you get the following content if profile.yaml
:
version: v1
type: profile
name: my-first-profile
context:
provider: github
alert: "on"
remediate: "on"
repository:
- type: secret_scanning
name: "secret_scanning_github" # Optional, as there aren't multiple rules
# of the same type under the entity - repository
def:
enabled: true
Finally, create your profile in Minder:
minder profile create -f profile.yaml
Check the status of your profile and see which repositories satisfy the rules by running:
minder profile status list --name my-first-profile --detailed
At the moment, the profile status list
with the --detailed
flag lists all the repositories that match the rules.
To get a more detailed view of the profile status, use the -o json
flag to get the output in JSON format and then
filter the output using jq
. For example, to get all rules that pertain to the repository minder
and have failed,
run the following command:
minder profile status list --name stacklok-remediate-profile -d -ojson 2>/dev/null | jq -C '.ruleEvaluationStatus | map(select(.entityInfo.repo_name == "minder" and .status == "failure"))'
Defining Rule Names in Profiles
In Minder profiles, rules are identified by their type and, optionally, a unique name.
Rule Types vs Rule Names
Rule types are mandatory and refer to the kind of rule being applied. Rule names, on the other hand, are optional identifiers that become crucial when multiple rules of the same type exist under an entity.
repository:
- type: secret_scanning
name: "secret_scanning_github"
def:
enabled: true
In this example, secret_scanning
is the rule type and secret_scanning_github
is the rule name.
When are Rule Names Mandatory?
If you're using multiple rules of the same type under an entity, each rule must have a unique name. This helps distinguish between rules and understand their specific purpose.
repository:
- type: secret_scanning
name: "secret_scanning_github"
def:
enabled: true
- type: secret_scanning
name: "secret_scanning_github_2"
def:
enabled: false
Here, we have two rules of the same type secret_scanning
under the repository
entity. Each rule has a unique name.
Uniqueness of Rule Names
No two rules, whether of the same type or different types, can have the same name under an entity. This avoids confusion and ensures each rule can be individually managed.
repository: # Would return an error while creating
- type: secret_scanning
name: "protect_github"
def:
enabled: true
- type: secret_push_protection
name: "protect_github"
def:
enabled: false
In the above used example, even though the rules are of different types (secret_scanning
and secret_push_protection
),
Minder will return an error while creating this profile as rule names are same under the same entity.
You may use same rule names under different entities (repository, artifacts, etc.)
Rule name should not match any rule type, except its own rule type. If a rule name matches its own rule type, it should not conflict with any other rule name under the same entity, including default rule names. Example:
repository: # Would return an error while creating
- type: dependabot_configured
name: "dependabot_configured"
def:
package_ecosystem: gomod
schedule_interval: daily
apply_if_file: go.mod
- type: dependabot_configured # default 'name' would be 'dependabot_configured'
def:
package_ecosystem: npm
schedule_interval: daily
apply_if_file: docs/package.json
In the above used example, even though the rules names appear different visually, Minder will return an error while
creating this profile as the rule name for npm
rule would be dependabot_configured
internally, which is same as
the explicit name of the gomod
rule.
Example
Consider a profile with two dependabot_configured
rules under the repository
entity. The first rule has a unique
name, "Dependabot Configured for GoLang". The second rule doesn't have a name, which is acceptable as Minder would
add rule type as the default name for the rule.
repository:
- type: dependabot_configured
name: "Dependabot Configured for GoLang"
def:
package_ecosystem: gomod
schedule_interval: daily
apply_if_file: go.mod
- type: dependabot_configured # default 'name' would be 'dependabot_configured'
def:
package_ecosystem: npm
schedule_interval: daily
apply_if_file: docs/package.json
You can find the rule definitions used above and many profile examples at minder-rules-and-profiles repository.