Skip to content

Commit

Permalink
Remove all punctuation from anchors in policies.md
Browse files Browse the repository at this point in the history
  • Loading branch information
zephyrdb committed Feb 13, 2025
1 parent c195eb2 commit 3f12535
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 16 deletions.
34 changes: 27 additions & 7 deletions examples/policies-no-rego.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* [P1005: Pods must not run with access to the host IPC](#p1005-pods-must-not-run-with-access-to-the-host-ipc)
* [P1006: Pods must not run with access to the host networking](#p1006-pods-must-not-run-with-access-to-the-host-networking)
* [P1007: Pods must not run with access to the host PID namespace](#p1007-pods-must-not-run-with-access-to-the-host-pid-namespace)
* [P1008: Pods must run as non-root](#p1008-pods-must-run-as-non-root)
* [P1008: Pods must run as non\-root](#p1008-pods-must-run-as-non-root)
* [P1009: PodSecurityPolicies must require all capabilities are dropped](#p1009-podsecuritypolicies-must-require-all-capabilities-are-dropped)
* [P1010: PodSecurityPolicies must not allow privileged escalation](#p1010-podsecuritypolicies-must-not-allow-privileged-escalation)
* [P1011: PodSecurityPolicies must not allow access to the host aliases](#p1011-podsecuritypolicies-must-not-allow-access-to-the-host-aliases)
Expand All @@ -21,13 +21,14 @@
* [P2001: Images must not use the latest tag](#p2001-images-must-not-use-the-latest-tag)
* [P2002: Containers must define resource constraints](#p2002-containers-must-define-resource-constraints)
* [P2005: Roles must not allow use of privileged PodSecurityPolicies](#p2005-roles-must-not-allow-use-of-privileged-podsecuritypolicies)
* [P2006: Tenants' containers must not run as privileged](#p2006-tenants'-containers-must-not-run-as-privileged)
* [P2006: Tenants' containers must not run as privileged](#p2006-tenants-containers-must-not-run-as-privileged)

## Warnings

* [P0001: Deprecated Deployment and DaemonSet API](#p0001-deprecated-deployment-and-daemonset-api)
* [P0003: Title \_wórds\_ with\-punct\_uation\_ \!"\#$%&'\(\)\*\+,\./:;\<=\>?@\[\\\]^\`\{\|\}~mark ](#p0003-title-_wórds_-with-punct_uation_-mark)
* [P2003: Containers should not have a writable root filesystem](#p2003-containers-should-not-have-a-writable-root-filesystem)
* [P2004: PodSecurityPolicies should require that a read-only root filesystem is set](#p2004-podsecuritypolicies-should-require-that-a-read-only-root-filesystem-is-set)
* [P2004: PodSecurityPolicies should require that a read\-only root filesystem is set](#p2004-podsecuritypolicies-should-require-that-a-read-only-root-filesystem-is-set)

## P0002: Required Labels

Expand Down Expand Up @@ -168,7 +169,7 @@ boundary.

_source: [pod_deny_host_pid](pod_deny_host_pid)_

## P1008: Pods must run as non-root
## P1008: Pods must run as non\-root

**Severity:** Violation

Expand Down Expand Up @@ -341,7 +342,7 @@ _source: [container_deny_without_resource_constraints](container_deny_without_re

**Resources:**

- rbac.authorization.k8s.io/Role
- rbac\.authorization\.k8s\.io/Role

Workloads not running in the exempted namespaces must not use PodSecurityPolicies with privileged permissions.

Expand All @@ -359,7 +360,7 @@ _source: [role_deny_use_privileged_psps](role_deny_use_privileged_psps)_
- apps/Deployment
- apps/StatefulSet

**MatchLabels:** is-tenant=true
**MatchLabels:** is\-tenant=true

Privileged containers can easily escalate to root privileges on the node. As
such containers running as privileged or with sufficient capabilities granted
Expand All @@ -386,6 +387,25 @@ the version for both of these resources must be `apps/v1`.

_source: [any_warn_deprecated_api_versions](any_warn_deprecated_api_versions)_

## P0003: Title \_wórds\_ with\-punct\_uation\_ \!"\#$%&'\(\)\*\+,\./:;\<=\>?@\[\\\]^\`\{\|\}~mark

**Severity:** Warning

**Resources:**

- \*/Pod

**MatchLabels:** \_test\_=true

**Parameters:**

* \_param\_name\_: array of string

This is only here to test and illustrate _punctuation_ / Markdown handling


_source: [policy_markdown_punctuation](policy_markdown_punctuation)_

## P2003: Containers should not have a writable root filesystem

**Severity:** Warning
Expand All @@ -403,7 +423,7 @@ important to make the root filesystem read-only.

_source: [container_warn_no_ro_fs](container_warn_no_ro_fs)_

## P2004: PodSecurityPolicies should require that a read-only root filesystem is set
## P2004: PodSecurityPolicies should require that a read\-only root filesystem is set

**Severity:** Warning

Expand Down
50 changes: 43 additions & 7 deletions examples/policies.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* [P1005: Pods must not run with access to the host IPC](#p1005-pods-must-not-run-with-access-to-the-host-ipc)
* [P1006: Pods must not run with access to the host networking](#p1006-pods-must-not-run-with-access-to-the-host-networking)
* [P1007: Pods must not run with access to the host PID namespace](#p1007-pods-must-not-run-with-access-to-the-host-pid-namespace)
* [P1008: Pods must run as non-root](#p1008-pods-must-run-as-non-root)
* [P1008: Pods must run as non\-root](#p1008-pods-must-run-as-non-root)
* [P1009: PodSecurityPolicies must require all capabilities are dropped](#p1009-podsecuritypolicies-must-require-all-capabilities-are-dropped)
* [P1010: PodSecurityPolicies must not allow privileged escalation](#p1010-podsecuritypolicies-must-not-allow-privileged-escalation)
* [P1011: PodSecurityPolicies must not allow access to the host aliases](#p1011-podsecuritypolicies-must-not-allow-access-to-the-host-aliases)
Expand All @@ -21,13 +21,14 @@
* [P2001: Images must not use the latest tag](#p2001-images-must-not-use-the-latest-tag)
* [P2002: Containers must define resource constraints](#p2002-containers-must-define-resource-constraints)
* [P2005: Roles must not allow use of privileged PodSecurityPolicies](#p2005-roles-must-not-allow-use-of-privileged-podsecuritypolicies)
* [P2006: Tenants' containers must not run as privileged](#p2006-tenants'-containers-must-not-run-as-privileged)
* [P2006: Tenants' containers must not run as privileged](#p2006-tenants-containers-must-not-run-as-privileged)

## Warnings

* [P0001: Deprecated Deployment and DaemonSet API](#p0001-deprecated-deployment-and-daemonset-api)
* [P0003: Title \_wórds\_ with\-punct\_uation\_ \!"\#$%&'\(\)\*\+,\./:;\<=\>?@\[\\\]^\`\{\|\}~mark ](#p0003-title-_wórds_-with-punct_uation_-mark)
* [P2003: Containers should not have a writable root filesystem](#p2003-containers-should-not-have-a-writable-root-filesystem)
* [P2004: PodSecurityPolicies should require that a read-only root filesystem is set](#p2004-podsecuritypolicies-should-require-that-a-read-only-root-filesystem-is-set)
* [P2004: PodSecurityPolicies should require that a read\-only root filesystem is set](#p2004-podsecuritypolicies-should-require-that-a-read-only-root-filesystem-is-set)

## P0002: Required Labels

Expand Down Expand Up @@ -383,7 +384,7 @@ pod_has_hostpid if {

_source: [pod_deny_host_pid](pod_deny_host_pid)_

## P1008: Pods must run as non-root
## P1008: Pods must run as non\-root

**Severity:** Violation

Expand Down Expand Up @@ -812,7 +813,7 @@ _source: [container_deny_without_resource_constraints](container_deny_without_re

**Resources:**

- rbac.authorization.k8s.io/Role
- rbac\.authorization\.k8s\.io/Role

Workloads not running in the exempted namespaces must not use PodSecurityPolicies with privileged permissions.

Expand Down Expand Up @@ -872,7 +873,7 @@ _source: [role_deny_use_privileged_psps](role_deny_use_privileged_psps)_
- apps/Deployment
- apps/StatefulSet

**MatchLabels:** is-tenant=true
**MatchLabels:** is\-tenant=true

Privileged containers can easily escalate to root privileges on the node. As
such containers running as privileged or with sufficient capabilities granted
Expand Down Expand Up @@ -954,6 +955,41 @@ warn contains msg if {

_source: [any_warn_deprecated_api_versions](any_warn_deprecated_api_versions)_

## P0003: Title \_wórds\_ with\-punct\_uation\_ \!"\#$%&'\(\)\*\+,\./:;\<=\>?@\[\\\]^\`\{\|\}~mark

**Severity:** Warning

**Resources:**

- \*/Pod

**MatchLabels:** \_test\_=true

**Parameters:**

* \_param\_name\_: array of string

This is only here to test and illustrate _punctuation_ / Markdown handling

### Rego

```rego
package policy_markdown_punctuation
import data.lib.core
import future.keywords.contains
import future.keywords.if
policyID := "P0003"
warn contains msg if {
core.apiVersion == "foo/bar"
msg := core.format_with_id("Title tester", policyID)
}
```

_source: [policy_markdown_punctuation](policy_markdown_punctuation)_

## P2003: Containers should not have a writable root filesystem

**Severity:** Warning
Expand Down Expand Up @@ -1003,7 +1039,7 @@ no_read_only_filesystem(container) if {

_source: [container_warn_no_ro_fs](container_warn_no_ro_fs)_

## P2004: PodSecurityPolicies should require that a read-only root filesystem is set
## P2004: PodSecurityPolicies should require that a read\-only root filesystem is set

**Severity:** Warning

Expand Down
31 changes: 31 additions & 0 deletions examples/policy_markdown_punctuation/src.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# METADATA
# title: " Title _wórds_ with-punct_uation_ !\"#$%&'()*+,./:;<=>?@[\\]^`{|}~mark "
# description: This is only here to test and illustrate _punctuation_ /
# Markdown handling
# custom:
# matchers:
# kinds:
# - apiGroups:
# - "*"
# kinds:
# - Pod
# labelSelector:
# matchLabels:
# _test_: "true"
# parameters:
# _param_name_:
# type: array
# items:
# type: string
package policy_markdown_punctuation

import data.lib.core
import future.keywords.contains
import future.keywords.if

policyID := "P0003"

warn contains msg if {
core.apiVersion == "foo/bar"
msg := core.format_with_id("Title tester", policyID)
}
7 changes: 7 additions & 0 deletions examples/policy_markdown_punctuation/src_test.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package policy_markdown_punctuation

import future.keywords.if

test_ignoreme if {
count(warn) == 1 with input as {"apiVersion": "foo/bar"}
}
65 changes: 63 additions & 2 deletions internal/commands/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"text/template"
"unicode"

"github.com/go-sprout/sprout/sprigin"
"github.com/plexsystems/konstraint/internal/rego"
Expand Down Expand Up @@ -39,6 +41,29 @@ type Document struct {
//go:embed document_template.tpl
var docTemplate string

var (
// One or more spaces
multiSpaceRE = regexp.MustCompile(` +`)

// Escape the characters on this list: https://www.markdownguide.org/basic-syntax/#characters-you-can-escape
// (Admittedly, a few of these seem... odd.)
markdownReplacer = strings.NewReplacer(
"\\", "\\\\", "`", "\\`", "*", "\\*", "_", "\\_", "{", "\\{", "}", "\\}", "[", "\\[", "]", "\\]", "<", "\\<",
">", "\\>", "(", "\\(", ")", "\\)", "#", "\\#", "+", "\\+", "-", "\\-", ".", "\\.", "!", "\\!", "|", "\\|",
)

// Space -> -, remove all ASCII punctuation except - and _
//
// (This is part of the GitHub anchor algorithm, but see below regarding tabs and other whitespace. Ref:
// https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links)
anchorReplacer = strings.NewReplacer(
" ", "-",
"!", "", "\"", "", "#", "", "$", "", "%", "", "&", "", "'", "", "(", "", ")", "", "*", "", "+", "", ",", "",
".", "", "/", "", ":", "", ";", "", "<", "", "=", "", ">", "", "?", "", "@", "", "[", "", "\\", "", "]", "",
"^", "", "`", "", "{", "", "|", "", "}", "", "~", "",
)
)

func newDocCommand() *cobra.Command {
cmd := cobra.Command{
Use: "doc <dir>",
Expand Down Expand Up @@ -186,8 +211,37 @@ func getDocumentation(path string, outputDirectory string) (map[rego.Severity][]
documentTitle = fmt.Sprintf("%s: %s", policy.PolicyID(), documentTitle)
}

anchor := strings.ToLower(strings.ReplaceAll(documentTitle, " ", "-"))
anchor = strings.ReplaceAll(anchor, ":", "")
// Tabs in Markdown headings are handled inconsistently across different parsers when it comes to matching
// anchors; some expect them to be removed (which is what GitHub does), while some expect them to be changed to
// -. Changing tabs to spaces beforehand solves that problem, and we'll handle other whitespace the same way
// just in case. Incidentally, handling of other Unicode characters varies; for instance, letters with accents
// are handled normally, but emoji seem to break some parsers, and a circled s (ⓢ, U+24E2) works, but
// markdownlint complains about it. Here, I think, is the place to draw the line in terms of how much
// intervention to do to be sure the anchors work.
var spacedDocumentTitle strings.Builder
for _, rune := range documentTitle {
if unicode.IsSpace(rune) {
spacedDocumentTitle.WriteString(" ")
} else {
spacedDocumentTitle.WriteString(string(rune))
}
}
documentTitle = spacedDocumentTitle.String()

// Similarly, parsers differ in whether they collapse multiple spaces when matching anchors. The safe thing is
// to collapse them before it can become an issue.
documentTitle = multiSpaceRE.ReplaceAllString(documentTitle, " ")

// The GitHub anchor algorithm says that Markdown is removed before conversion. However, '_foo_bar_' counts as
// 'foo_bar' in italics (which are removed), whereas 'foo_bar' is just text. Since only full parsing can
// determine what is actually functional Markdown, it's safest to just escape all of the Markdown characters.
// (That means that Markdown won't work in titles, but it's probably a reasonable tradeoff.) Plus, of course,
// some characters (such as []) would actually break the generated link otherwise.
documentTitle = markdownReplacer.Replace(documentTitle)

// Skip non-U+0020-whitespace and Markdown removal because we handled them above. Ref:
// https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links
anchor := anchorReplacer.Replace(strings.TrimSpace(strings.ToLower(documentTitle)))

legacyMatchers, err := policy.Matchers()
if err != nil {
Expand All @@ -209,13 +263,17 @@ func getDocumentation(path string, outputDirectory string) (map[rego.Severity][]
logger.Warn("No kind matchers set, this can lead to poor policy performance.")
matchResources = append(matchResources, "Any Resource")
}
for i := range matchResources {
matchResources[i] = markdownReplacer.Replace(matchResources[i])
}

var matchLabels string
if policy.AnnotationLabelSelectorMatcher() != nil {
matchLabels = labelSelectorDocString(policy.AnnotationLabelSelectorMatcher())
} else {
matchLabels = legacyMatchers.MatchLabelsMatcher.String()
}
matchLabels = markdownReplacer.Replace(matchLabels)

parameters := policy.Parameters()
if len(policy.AnnotationParameters()) > 0 {
Expand All @@ -224,6 +282,9 @@ func getDocumentation(path string, outputDirectory string) (map[rego.Severity][]
sort.Slice(parameters, func(i, j int) bool {
return parameters[i].Name < parameters[j].Name
})
for i := range parameters {
parameters[i].Name = markdownReplacer.Replace(parameters[i].Name)
}

header := Header{
Title: documentTitle,
Expand Down

0 comments on commit 3f12535

Please sign in to comment.