Skip to content

Commit 513f89f

Browse files
authored
feat: expiration on security.txt and fix for SBOM step, to ensure the correct files are committed (#91)
* feat: adding a CI step to check expiration on security.txt and a fix for SBOM step, to ensure the correct files are committed * ensure execution skips for repos that are not targeted * version increment for the new orb; thanks for feedback
1 parent 5208ac4 commit 513f89f

7 files changed

Lines changed: 94 additions & 4 deletions

File tree

‎README.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ project settings CircleCI. Then include the following in your `.circleci/config.
1616
version: 2.1
1717
setup: true
1818
orbs:
19-
build: mojaloop/build@2.1.7
19+
build: mojaloop/build@2.1.8
2020
workflows:
2121
setup:
2222
jobs:
@@ -39,6 +39,7 @@ workflows:
3939
# test_integration_resource_class: medium
4040
# test_functional_resource_class: medium
4141
# grype_scan_resource_class: medium
42+
# check_security_txt_expiry_resource_class: medium
4243
# release_resource_class: medium
4344
# github_release_resource_class: medium
4445
# github_prerelease_resource_class: medium
@@ -140,6 +141,9 @@ This feature is particularly useful for:
140141

141142
- If a `Dockerfile` is present in the root of the repository, it will be used to
142143
build and publish an image.
144+
- If a `.well-known/security.txt` file is present (RFC 9116), its `Expires`
145+
field is validated and the build fails once that date has passed. Repos with
146+
no security.txt, or one with no `Expires` field, are unaffected.
143147
- Published Docker images are signed using Cosign with CircleCI OIDC. Docker image
144148
signing requires a CircleCI environment CLI that supports
145149
`circleci run oidc get --claims`. The minimum tested CircleCI Build Agent version
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
description: >
2+
Validates the `Expires` field of `.well-known/security.txt` (RFC 9116).
3+
4+
If no security.txt file exists in the repo, the step halts (passes) so repos
5+
that haven't adopted one yet are unaffected. If the file exists but has no
6+
`Expires` field, this passes with a warning (the field is recommended, not
7+
universally adopted yet). If `Expires` is present and its date is in the
8+
past, the step fails the job.
9+
steps:
10+
- run:
11+
name: Check security.txt expiration
12+
command: |
13+
SECURITY_TXT=".well-known/security.txt"
14+
15+
if [ ! -f "$SECURITY_TXT" ]; then
16+
echo "No $SECURITY_TXT found, skipping check."
17+
circleci-agent step halt
18+
exit 0
19+
fi
20+
21+
node -e '
22+
const fs = require("fs");
23+
const path = ".well-known/security.txt";
24+
const content = fs.readFileSync(path, "utf8");
25+
const match = content.match(/^Expires:\s*(.+)$/mi);
26+
27+
if (!match) {
28+
console.warn("WARNING: " + path + " has no Expires field (RFC 9116 requires one). Skipping.");
29+
process.exit(0);
30+
}
31+
32+
const raw = match[1].trim();
33+
const expires = new Date(raw);
34+
35+
if (isNaN(expires.getTime())) {
36+
console.error("ERROR: could not parse Expires value in " + path + ": \"" + raw + "\"");
37+
console.error("Expected an RFC 3339 date-time, e.g. 2027-09-18T00:00:00.000Z");
38+
process.exit(1);
39+
}
40+
41+
const now = new Date();
42+
const daysDiff = Math.floor((expires.getTime() - now.getTime()) / 86400000);
43+
44+
if (expires.getTime() < now.getTime()) {
45+
console.error("FAIL: " + path + " expired on " + expires.toISOString() + " (" + Math.abs(daysDiff) + " day(s) ago).");
46+
console.error("Update the Expires field in " + path + ".");
47+
process.exit(1);
48+
}
49+
50+
console.log("OK: " + path + " valid until " + expires.toISOString() + " (" + daysDiff + " day(s) remaining).");
51+
'

‎src/examples/build_and_test.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ usage:
44
version: 2.1
55
setup: true
66
orbs:
7-
build: mojaloop/build@2.1.7
7+
build: mojaloop/build@2.1.8
88
workflows:
99
setup:
1010
jobs:
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
description: >
2+
Validates that .well-known/security.txt (RFC 9116), if present in the repo,
3+
has not expired. Passes with no effect for repos that don't have one yet.
4+
executor:
5+
name: docker
6+
resource_class: << parameters.resource_class >>
7+
parameters:
8+
resource_class:
9+
type: enum
10+
enum: ["small", "medium", "medium+", "large", "xlarge", "2xlarge", "2xlarge+"]
11+
default: medium
12+
steps:
13+
- docker_dependencies
14+
- checkout
15+
- check_security_txt_expiry

‎src/jobs/github_release.yml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ steps:
6060
echo "Generating SBOM using NPM"
6161
npx -y --package @mojaloop/ml-depcheck-utility@1.1.3 generate-sbom-npm
6262
fi
63-
git add sbom-*.csv
63+
# Quoted pathspec so git (not the shell) resolves the glob — this stages
64+
# the removal of any old sbom-*.csv already deleted from disk above,
65+
# not just the newly generated file.
66+
git add -A -- 'sbom-*.csv'
6467
git checkout -- . 2>/dev/null || true
6568
git commit -m "chore(sbom): update sbom [skip ci]" || echo "No changes to commit"
6669
git push origin "${CIRCLE_BRANCH}" || echo "Git push failed"

‎src/jobs/workflow.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ parameters:
5353
type: enum
5454
enum: ["small", "medium", "medium+", "large", "xlarge", "2xlarge", "2xlarge+"]
5555
default: medium
56+
check_security_txt_expiry_resource_class:
57+
type: enum
58+
enum: ["small", "medium", "medium+", "large", "xlarge", "2xlarge", "2xlarge+"]
59+
default: medium
5660
release_resource_class:
5761
type: enum
5862
enum: ["small", "medium", "medium+", "large", "xlarge", "2xlarge", "2xlarge+"]
@@ -112,6 +116,7 @@ steps:
112116
"test_integration_resource_class":"<< parameters.test_integration_resource_class>>",
113117
"test_functional_resource_class":"<< parameters.test_functional_resource_class>>",
114118
"grype_scan_resource_class":"<< parameters.grype_scan_resource_class>>",
119+
"check_security_txt_expiry_resource_class":"<< parameters.check_security_txt_expiry_resource_class>>",
115120
"release_resource_class":"<< parameters.release_resource_class>>",
116121
"github_release_resource_class":"<< parameters.github_release_resource_class>>",
117122
"github_prerelease_resource_class":"<< parameters.github_prerelease_resource_class>>",

‎src/workflows/build_and_test.yml‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
version: 2.1
22
orbs:
3-
build: mojaloop/build@2.1.7
3+
build: mojaloop/build@2.1.8
44
parameters:
55
git_tag:
66
type: string
@@ -69,6 +69,10 @@ parameters:
6969
type: enum
7070
enum: ["small", "medium", "medium+", "large", "xlarge", "2xlarge", "2xlarge+"]
7171
default: medium
72+
check_security_txt_expiry_resource_class:
73+
type: enum
74+
enum: ["small", "medium", "medium+", "large", "xlarge", "2xlarge", "2xlarge+"]
75+
default: medium
7276
release_resource_class:
7377
type: enum
7478
enum: ["small", "medium", "medium+", "large", "xlarge", "2xlarge", "2xlarge+"]
@@ -173,6 +177,12 @@ workflows:
173177
<<: *test-condition
174178
name: License scan
175179
resource_class: << pipeline.parameters.license_scan_resource_class >>
180+
- build/check_security_txt_expiry:
181+
name: Security.txt expiry
182+
resource_class: << pipeline.parameters.check_security_txt_expiry_resource_class >>
183+
filters: *setup-filter
184+
requires:
185+
- Setup
176186
- build/build_local:
177187
<<: *test-condition
178188
name: Build
@@ -220,6 +230,7 @@ workflows:
220230
- Vulnerability check
221231
- License scan
222232
- Grype scan
233+
- Security.txt expiry
223234
- build/github_release:
224235
name: GitHub release
225236
resource_class: << pipeline.parameters.github_release_resource_class >>
@@ -292,6 +303,7 @@ workflows:
292303
- Functional tests
293304
- License scan
294305
- Grype scan
306+
- Security.txt expiry
295307
- build/publish_docker_prerelease:
296308
name: Docker prerelease
297309
resource_class: << pipeline.parameters.publish_docker_prerelease_resource_class >>

0 commit comments

Comments
 (0)