crap4ruby 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -1
- data/CONTRIBUTING.md +12 -2
- data/README.md +42 -18
- data/lib/crap4ruby/analyzer.rb +177 -29
- data/lib/crap4ruby/cli.rb +44 -23
- data/lib/crap4ruby/coverage.rb +13 -9
- data/lib/crap4ruby/discovery.rb +22 -5
- data/lib/crap4ruby/report.rb +14 -4
- data/lib/crap4ruby/score.rb +5 -2
- data/lib/crap4ruby/version.rb +1 -1
- data/lib/crap4ruby.rb +4 -4
- data/oss-audit-report.before.md +159 -0
- data/oss-audit-report.md +110 -0
- data/scoring-audit.md +251 -0
- data/sig/crap4ruby.rbs +31 -15
- metadata +4 -1
data/lib/crap4ruby/discovery.rb
CHANGED
|
@@ -22,6 +22,27 @@ module Crap4Ruby
|
|
|
22
22
|
# @raise [SystemCallError] if paths cannot be read or resolved
|
|
23
23
|
def self.call(root:, source_roots:, paths:, changed:)
|
|
24
24
|
existing, filters = paths.partition { |path| File.exist?(File.expand_path(path, root)) }
|
|
25
|
+
roots = selection_roots(root, source_roots, existing)
|
|
26
|
+
files = roots.flat_map { |path| File.file?(path) ? [path] : walk(path) }
|
|
27
|
+
files = files.select { |path| File.extname(path) == ".rb" }.map { |path| File.realpath(path) }.uniq.sort
|
|
28
|
+
unless filters.empty?
|
|
29
|
+
files.select! do |path|
|
|
30
|
+
filters.any? do |filter|
|
|
31
|
+
path.delete_prefix("#{root}/").include?(filter)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
files &= changed_files(root) if changed
|
|
36
|
+
files
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Validates directory roots and combines them with explicit paths.
|
|
40
|
+
#
|
|
41
|
+
# @param root [String] absolute project directory
|
|
42
|
+
# @param source_roots [Array<String>] explicit source directories
|
|
43
|
+
# @param existing [Array<String>] existing positional paths
|
|
44
|
+
# @return [Array<String>] expanded roots, defaulting to the project directory
|
|
45
|
+
def self.selection_roots(root, source_roots, existing)
|
|
25
46
|
roots = source_roots.map do |path|
|
|
26
47
|
directory = File.expand_path(path, root)
|
|
27
48
|
raise Error, "#{path}: source root must be a directory" unless File.directory?(directory)
|
|
@@ -30,11 +51,7 @@ module Crap4Ruby
|
|
|
30
51
|
end
|
|
31
52
|
roots.concat(existing.map { |path| File.expand_path(path, root) })
|
|
32
53
|
roots << root if roots.empty?
|
|
33
|
-
|
|
34
|
-
files = files.select { |path| File.extname(path) == ".rb" }.map { |path| File.realpath(path) }.uniq.sort
|
|
35
|
-
files.select! { |path| filters.any? { |filter| path.delete_prefix("#{root}/").include?(filter) } } unless filters.empty?
|
|
36
|
-
files &= changed_files(root) if changed
|
|
37
|
-
files
|
|
54
|
+
roots
|
|
38
55
|
end
|
|
39
56
|
|
|
40
57
|
# Walks a directory without following symlinks or entering excluded directories.
|
data/lib/crap4ruby/report.rb
CHANGED
|
@@ -21,16 +21,26 @@ module Crap4Ruby
|
|
|
21
21
|
# @return [String] table with one decimal place for percentages and scores, ending with a newline
|
|
22
22
|
def self.table(entries)
|
|
23
23
|
rows = [["Function", "Namespace", "CC", "Cov%", "CRAP"]] + entries.map do |entry|
|
|
24
|
-
[entry.name, entry.namespace, entry.complexity.to_s,
|
|
24
|
+
[label(entry.name), label(entry.namespace), entry.complexity.to_s,
|
|
25
25
|
entry.coverage ? format("%.1f%%", entry.coverage) : "N/A",
|
|
26
26
|
entry.crap ? format("%.1f", entry.crap) : "N/A"]
|
|
27
27
|
end
|
|
28
28
|
widths = rows.transpose.map { |column| column.map(&:length).max }
|
|
29
29
|
lines = rows.map do |row|
|
|
30
|
-
row.each_with_index.map
|
|
30
|
+
row.each_with_index.map do |value, index|
|
|
31
|
+
index < 2 ? value.ljust(widths[index]) : value.rjust(widths[index])
|
|
32
|
+
end.join(" ")
|
|
31
33
|
end
|
|
32
34
|
lines.insert(1, "-" * lines.first.length)
|
|
33
|
-
(["CRAP Report", "==========="] + lines).join("\n")
|
|
35
|
+
"#{(["CRAP Report", "==========="] + lines).join("\n")}\n"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Keeps names with control characters on one terminal line.
|
|
39
|
+
#
|
|
40
|
+
# @param value [String] method name, namespace, or diagnostic label
|
|
41
|
+
# @return [String] original text, or a quoted JSON string when escaping is needed
|
|
42
|
+
def self.label(value)
|
|
43
|
+
value.match?(/[[:cntrl:]]/) ? JSON.generate(value) : value
|
|
34
44
|
end
|
|
35
45
|
|
|
36
46
|
# Serializes the crapper-compatible snapshot, preserving entry order.
|
|
@@ -48,7 +58,7 @@ module Crap4Ruby
|
|
|
48
58
|
|
|
49
59
|
# Formats an EDN number with at most four decimal places and at least one.
|
|
50
60
|
#
|
|
51
|
-
# @param value [Float, nil] percentage or CRAP score
|
|
61
|
+
# @param value [Float, Rational, nil] percentage or CRAP score
|
|
52
62
|
# @return [String] decimal representation, or `nil` for an unknown value
|
|
53
63
|
def self.number(value)
|
|
54
64
|
return "nil" if value.nil?
|
data/lib/crap4ruby/score.rb
CHANGED
|
@@ -11,8 +11,11 @@ module Crap4Ruby
|
|
|
11
11
|
# when coverage is disabled or the method has no measurable lines
|
|
12
12
|
def self.call(method, coverage)
|
|
13
13
|
fraction = coverage&.fraction(method)
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
if fraction
|
|
15
|
+
uncovered = 1 - fraction
|
|
16
|
+
crap = (method.complexity * method.complexity * uncovered * uncovered * uncovered) + method.complexity
|
|
17
|
+
end
|
|
18
|
+
Entry.new(method.name, method.namespace, method.complexity, fraction && (fraction * 100).to_f, crap, method.path)
|
|
16
19
|
end
|
|
17
20
|
end
|
|
18
21
|
end
|
data/lib/crap4ruby/version.rb
CHANGED
data/lib/crap4ruby.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Crap4Ruby
|
|
|
12
12
|
class Error < StandardError
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
# Immutable static analysis of one
|
|
15
|
+
# Immutable static analysis of one method definition.
|
|
16
16
|
MethodInfo = Data.define(:name, :namespace, :complexity, :lines, :path)
|
|
17
17
|
|
|
18
18
|
# Immutable analysis result with coverage and a CRAP score.
|
|
@@ -24,9 +24,9 @@ module Crap4Ruby
|
|
|
24
24
|
# attr_reader :name
|
|
25
25
|
# # @return [String] lexical owner, or `Object` for a top-level method
|
|
26
26
|
# attr_reader :namespace
|
|
27
|
-
# # @return [Integer] cyclomatic complexity
|
|
27
|
+
# # @return [Integer] cyclomatic complexity of the body
|
|
28
28
|
# attr_reader :complexity
|
|
29
|
-
# # @return [Array<Integer
|
|
29
|
+
# # @return [Array<Integer>, nil] one-based body lines, or nil when line ownership is ambiguous
|
|
30
30
|
# attr_reader :lines
|
|
31
31
|
# # @return [String] source path supplied to the analyzer
|
|
32
32
|
# attr_reader :path
|
|
@@ -40,7 +40,7 @@ module Crap4Ruby
|
|
|
40
40
|
# attr_reader :complexity
|
|
41
41
|
# # @return [Float, nil] percentage from 0 to 100, or nil when unmeasurable or disabled
|
|
42
42
|
# attr_reader :coverage
|
|
43
|
-
# # @return [Float, nil] CRAP score, or nil when coverage is unknown
|
|
43
|
+
# # @return [Rational, Float, nil] CRAP score, or nil when coverage is unknown
|
|
44
44
|
# attr_reader :crap
|
|
45
45
|
# # @return [String] source path supplied to the analyzer
|
|
46
46
|
# attr_reader :path
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Open source audit report
|
|
2
|
+
|
|
3
|
+
Generated by oss-audit on 2026-10-28 for commit `663096766ff9e96cc7054a043f80ae3501a2854c`.
|
|
4
|
+
This report is untracked by default; the maintainer may commit or delete it.
|
|
5
|
+
|
|
6
|
+
## Repository
|
|
7
|
+
|
|
8
|
+
GitHub: [svyatov/crap4ruby](https://github.com/svyatov/crap4ruby). Default branch: `main`. The checkout was clean before this report.
|
|
9
|
+
|
|
10
|
+
Audited 42 applicable rules: 23 pass, 17 fail, 2 unknown; 19 not applicable.
|
|
11
|
+
The standard contains 61 rule headings, with 0 retired rules. All 61 active rules have one verdict.
|
|
12
|
+
|
|
13
|
+
Not applicable: seven agent-skill rules; two single-maintainer rules; one separate-question-channel rule; one dependency-cache rule; one GitLab-only rule; one missing-updater delay rule; one RubyGems install-script exemption; two forge-asset rules; one tag-published registry-entry rule; one no-removals rule; one package-major-identity rule.
|
|
14
|
+
|
|
15
|
+
Shipped ecosystems: RubyGems, package `crap4ruby`, registry-push track. The gemspec declares the package; release.yml publishes it; RubyGems serves version 0.1.0.
|
|
16
|
+
Present ecosystems: RubyGems, proved by `crap4ruby.gemspec`, `Gemfile`, and `Gemfile.lock`.
|
|
17
|
+
No other roster manifest or lockfile was found in the checkout. All eleven ecosystem reference files were read.
|
|
18
|
+
No container push appears in the workflows. Account package inventory is unknown because GitHub requires the unavailable read:packages scope.
|
|
19
|
+
|
|
20
|
+
Built forge-release assets: none on v0.1.0; the release API returns an empty asset list. The built gem is published to RubyGems only.
|
|
21
|
+
CI files: `.github/workflows/ci.yml` and `.github/workflows/release.yml`. Committed lockfile: `Gemfile.lock`.
|
|
22
|
+
|
|
23
|
+
Release flow: signed v* tag, three Ruby test jobs, gem build, required human approval in environment `release`, OIDC exchange, Sigstore signing, gem push, then GitHub release notes.
|
|
24
|
+
Release run [40668522014](https://github.com/svyatov/crap4ruby/actions/runs/40668522014) succeeded at the audited commit.
|
|
25
|
+
GitHub exposes one administrator, `svyatov`, and no teams. The release environment permits that human reviewer; main and tags have no rulesets.
|
|
26
|
+
|
|
27
|
+
The collector completed successfully. Its facts were checked against tracked files and live GitHub APIs.
|
|
28
|
+
Local CLI help and the prose scanner ran; the test suite was not rerun because this audit changed no executable code.
|
|
29
|
+
Ruby support scope was checked against [Ruby maintenance branches](https://www.ruby-lang.org/en/downloads/branches/).
|
|
30
|
+
The registry evidence came from [0.1.0 metadata](https://rubygems.org/api/v2/rubygems/crap4ruby/versions/0.1.0.json) and [its attestation API](https://rubygems.org/api/v1/attestations/crap4ruby-0.1.0.json).
|
|
31
|
+
|
|
32
|
+
Provenance verification limits: SHA256 `29cedba469c21114a8c6c2898303030990ff0f367250e620f2a42aaf57d76096` matches the downloaded gem and bundle.
|
|
33
|
+
The ECDSA signature verifies with the bundled certificate, whose identity names release.yml, tag v0.1.0, and the audited commit.
|
|
34
|
+
No complete Sigstore verifier was installed, and no new verification dependency was installed during the audit.
|
|
35
|
+
The Fulcio chain, certificate-transparency evidence, and Rekor proof remain unchecked; R-PUB-03 is therefore unknown.
|
|
36
|
+
|
|
37
|
+
## Prerequisites
|
|
38
|
+
|
|
39
|
+
No confirmed prerequisite requires a manual web form, password, or MFA step before the fix groups.
|
|
40
|
+
RubyGems trusted publishing and the GitHub release approval gate already worked in the observed release.
|
|
41
|
+
The two unknown verdicts require more evidence, not an assumption that either control failed.
|
|
42
|
+
No repository setting, dependency, workflow, or release was changed by this audit.
|
|
43
|
+
|
|
44
|
+
## Verdicts
|
|
45
|
+
|
|
46
|
+
- R-DOC-01 pass README.md:3 opens with one sentence identifying the tool and its Ruby-method scope.
|
|
47
|
+
- R-DOC-02 fail README.md:19-24 shows commands without visible example output or result comments.
|
|
48
|
+
Fixed by: oss-readme
|
|
49
|
+
Check: `README.md` contains a fenced code block with an install command, followed by a second fenced code block showing minimal usage whose result is visible in a comment inside the block or in a block immediately below it, and both appear before any section about design, motivation, or comparisons.
|
|
50
|
+
- R-DOC-03 pass README.md:176,189 links existing CONTRIBUTING.md, CHANGELOG.md, and LICENSE files.
|
|
51
|
+
- R-DOC-04 pass README.md:11,23,79-90,178 matches crap4ruby.gemspec, CLI option parsing, version.rb, and both workflow matrices; local --help confirmed the flags.
|
|
52
|
+
- R-DOC-05 pass README.md, CONTRIBUTING.md, and CHANGELOG.md passed oss-writing/scripts/prose.mjs with zero findings; manual heading review found no sentence-case violations.
|
|
53
|
+
- R-DOC-06 pass README.md:5,11,178 names Ruby, Prism, and CRuby 3.3/3.4/4.0; the gemspec, analyzer, and CI matrix confirm them.
|
|
54
|
+
- R-DOC-07 fail README.md:51-55 precedes the SimpleCov gem declaration with "test dependencies" but names no consuming file.
|
|
55
|
+
Fixed by: oss-readme
|
|
56
|
+
Check: every fenced code block in `README.md` carries a language tag, and every block whose destination is not a shell is preceded by a sentence naming the file, tool, or prompt it goes to.
|
|
57
|
+
- R-DOC-08 pass README.md:185 links public GitHub issues for both defects and questions.
|
|
58
|
+
- R-DOC-09 fail README.md has no maintenance or support-status statement; the GitHub repository is not archived.
|
|
59
|
+
Fixed by: oss-readme
|
|
60
|
+
Check: `README.md` carries a statement of maintenance or support status, or the repository carries an equivalent signal: `DEPRECATED` as the first heading of `README.md`, `DEPRECATED` at the start of the repository description, a no-maintenance-intended badge, or the archived flag set.
|
|
61
|
+
- R-DOC-10 fail README.md:3-7 lists features but presents none as a distinction from an alternative.
|
|
62
|
+
Fixed by: oss-readme
|
|
63
|
+
Check: `README.md`, before its first `##` heading, presents at least one claim as what distinguishes the project from the alternatives a reader already has, that claim names a supported boundary, a measured number, or an alternative project by name, and it is traceable to the source, a manifest, the CI configuration, or a linked measurement. A number stated somewhere in the opening for another purpose does not satisfy this.
|
|
64
|
+
- R-COM-01 pass LICENSE:1 contains MIT; crap4ruby.gemspec:11 declares MIT, as does RubyGems 0.1.0 metadata.
|
|
65
|
+
- R-COM-02 fail CONTRIBUTING.md:1-19 gives setup and tests but no pull-request submission instructions or link to an acceptance-criteria document.
|
|
66
|
+
Fixed by: oss-community
|
|
67
|
+
Check: `CONTRIBUTING.md` exists at the repository root, in `.github/`, or in `docs/` on GitHub, or at the repository root on GitLab, and states the setup command, the test command, how to open a pull request or merge request, that a change adding functionality arrives with a test, and which document states what an acceptable contribution must satisfy. That document exists at the path or URL the guide names.
|
|
68
|
+
- R-COM-03 fail CODE_OF_CONDUCT.md is absent from the root, .github/, and docs/.
|
|
69
|
+
Fixed by: oss-community
|
|
70
|
+
Check: `CODE_OF_CONDUCT.md` exists at the repository root, in `.github/`, or in `docs/` on GitHub, or at the repository root on GitLab, and contains an email address or a reporting URL with no template placeholder text.
|
|
71
|
+
- R-COM-04 fail SECURITY.md is absent from the root, .github/, and docs/.
|
|
72
|
+
Fixed by: oss-community
|
|
73
|
+
Check: `SECURITY.md` exists at the repository root, in `.github/`, or in `docs/` on GitHub, or at the repository root on GitLab, and names a private channel that an unaffiliated reporter can use, together with the time you commit to responding in, which is no longer than 14 days. Accept GitHub private vulnerability reporting only when the repository is public and the feature is enabled. Accept a GitLab confidential issue only when the intended reporter role can create it as confidential; GitLab Service Desk and a monitored security email are valid alternatives.
|
|
74
|
+
- R-COM-05 fail .github/ISSUE_TEMPLATE/ and all accepted pull-request-template paths are absent.
|
|
75
|
+
Fixed by: oss-community
|
|
76
|
+
Check: on GitHub, `.github/ISSUE_TEMPLATE/` holds at least one template, and a pull request template sits at any path GitHub reads: `pull_request_template.md` at the repository root, in `docs/`, or in `.github/`, or a `PULL_REQUEST_TEMPLATE/` directory holding at least one template in any of those three. On GitLab, `.gitlab/issue_templates/` holds at least one template and `.gitlab/merge_request_templates/` exists. Issue forms are read from `.github/ISSUE_TEMPLATE/` alone, so that path is exact where the pull request template's is not.
|
|
77
|
+
- R-COM-06 n/a One principal, svyatov, holds all push/maintain/admin access; the collaborators API lists only that account and the teams API returns [].
|
|
78
|
+
- R-COM-07 pass GitHub repository API returns a description, four topics, and homepage https://rubygems.org/gems/crap4ruby.
|
|
79
|
+
- R-COM-08 fail The tracked README and CONTRIBUTING guide state neither decision authority nor a succession arrangement.
|
|
80
|
+
Fixed by: oss-community
|
|
81
|
+
Check: a tracked file states how decisions get made and who makes them, and says what becomes of the project if the current maintainers stop, whether that is a named successor, an organization or foundation holding the repository, or a plain statement that the project has one maintainer and no succession arranged. A single-maintainer project satisfies this rule by saying so; naming a second maintainer is not required.
|
|
82
|
+
- R-COM-09 n/a No separate public question channel is advertised; GitHub Discussions is disabled and README.md:185 routes questions to issues.
|
|
83
|
+
- R-CI-01 pass .github/workflows/ci.yml:3-6 runs on main pushes and unfiltered pull_request events; main is the API-reported default branch.
|
|
84
|
+
- R-CI-02 pass CONTRIBUTING.md:7 and .github/workflows/ci.yml:34 both run bundle exec rake; Rakefile defines tests followed by the CRAP gate, with no separate lint/typecheck/build check.
|
|
85
|
+
- R-CI-03 pass rubygems, present: crap4ruby.gemspec:20 requires Ruby >=3.3; ci.yml:22 and release.yml:22 cover maintained Ruby 3.3, 3.4, and 4.0.
|
|
86
|
+
- R-CI-04 n/a Neither workflow defines a dependency cache; every ruby/setup-ruby invocation sets bundler-cache: false.
|
|
87
|
+
- R-CI-05 pass All five workflow jobs have explicit timeouts; ci.yml:11-13 cancels superseded pull-request runs.
|
|
88
|
+
- R-CI-06 pass test/*_test.rb contains automated tests; Rakefile:9-12 discovers them and default:crap invokes the suite.
|
|
89
|
+
- R-SEC-01 pass All 12 external uses entries across ci.yml and release.yml use full 40-character SHAs; the checkout ships no action.yml/action.yaml.
|
|
90
|
+
- R-SEC-02 pass Both workflows set contents: read at top level; only publish requests id-token: write and github-release requests contents: write.
|
|
91
|
+
- R-SEC-03 fail rubygems, present: no Dependabot or Renovate configuration covers Gemfile, Gemfile.lock, or GitHub Actions.
|
|
92
|
+
Fixed by: oss-harden
|
|
93
|
+
Check: the repository contains `.github/dependabot.yml` or a Renovate configuration, and its ecosystem list covers both the package manager the project uses and the CI action or container images it runs.
|
|
94
|
+
- R-SEC-04 fail GitHub rulesets API returns []; branches/main/protection returns 404 "Branch not protected".
|
|
95
|
+
Fixed by: oss-harden
|
|
96
|
+
Check: the default branch is protected, a pull request or merge request is the only path onto it, at least one CI status check must pass before merge, and force pushes and deletion are blocked. On GitHub the settings live in either of two places, so read both: `gh api repos/{owner}/{repo}/rulesets` for a ruleset, and `gh api repos/{owner}/{repo}/branches/{branch}/protection` for a classic rule, which answers `404 Branch not protected` when a ruleset is what guards the branch. On GitLab, `GET /projects/:id/protected_branches/:name`.
|
|
97
|
+
- R-SEC-05 pass After git fetch --tags, v0.1.0 is an annotated tag; git tag -v succeeds against svyatov's API-published SSH signing key and tagger email leonid@svyatov.com.
|
|
98
|
+
- R-SEC-06 n/a The repository uses GitHub, not GitLab.
|
|
99
|
+
- R-SEC-07 pass Workflow run blocks contain no github.event.* or github.head_ref interpolation and no pull_request_target/workflow_run trigger; tag arguments use quoted GITHUB_REF_NAME.
|
|
100
|
+
- R-SEC-08 fail rubygems, present: release.yml:77 installs sigstore-cli with gem install outside a committed lockfile; Gemfile.lock checksums and BUNDLE_FROZEN cover only the test jobs.
|
|
101
|
+
Fixed by: oss-harden
|
|
102
|
+
Check: the lockfile or fully hashed requirements file the project's package manager produces is committed, records integrity for registry dependencies, and every CI install uses the package manager's current frozen mode so it fails on stale or missing resolution data rather than updating it. For the npm ecosystem that is `npm ci`, `yarn install --immutable` (`--frozen-lockfile` on Yarn 1), `pnpm install --frozen-lockfile`, or `bun ci` / `bun install --frozen-lockfile`. Bun does not turn frozen mode on in CI by itself, so a bare `bun install`, including one a third-party action runs, fails the rule unless `bunfig.toml` sets `frozenLockfile = true` under `[install]`. Two clauses narrow it, and both key on what the tooling documents rather than on what the repository happens to contain, so an absent lockfile is never mistaken for an impossible one. Where the project's build tool publishes no lockfile format at all, the version-lock half falls outside this rule rather than failing it; among the ecosystems this standard covers that is Apache Maven, which offers fixed versions, `dependencyManagement`, and enforcer rules such as `banDynamicVersions` in place of one, and it is the build tool rather than the Maven Central registry that earns this, because a Gradle project publishing to the same registry writes `gradle.lockfile` and stays inside the rule. Where the ecosystem's own documentation directs a library package not to commit its lockfile, which both NuGet and Dart do, the rule reaches that ecosystem's application packages and the library case falls outside it rather than failing it.
|
|
103
|
+
- R-SEC-09 fail Ruby source is present; code-scanning/default-setup reports not-configured, no analysis workflow exists, and main requires no check.
|
|
104
|
+
Fixed by: oss-harden
|
|
105
|
+
Check: where the repository contains source in a language a static analyzer supports, a static analysis workflow runs on pull requests to the default branch and its result is a required check. A repository holding no source in a supported language falls outside this rule rather than failing it.
|
|
106
|
+
- R-SEC-10 pass GitHub repository API security_and_analysis reports secret_scanning and secret_scanning_push_protection enabled.
|
|
107
|
+
- R-SEC-11 fail rubygems, present: vulnerability-alerts returns 404 "Vulnerability alerts are disabled"; dependency-graph/sbom returns 404 and no advisory-scanner job exists.
|
|
108
|
+
Fixed by: oss-harden
|
|
109
|
+
Check: the package set the forge reports watching matches the project's resolved dependency set, so coverage of a manifest's direct dependencies does not read as coverage of the lockfile; where the forge cannot parse the project's lockfile, a scanner that can reads it and covers the residual; and where the forge's updater lists the ecosystem for version updates but not for security updates, which on GitHub is the case for both `bun` and `mix`, the residual is that ecosystem's whole advisory feed rather than a parsing gap, so the scanner is the only watcher the project has rather than a supplement to one. On GitHub, `gh api repos/{owner}/{repo}/vulnerability-alerts` answers 204 when enabled and 404 when disabled, and `gh api repos/{owner}/{repo}/dependency-graph/sbom` lists what is actually watched; read every control back after a write for the reason R-SEC-10 gives, and treat an absent `security_and_analysis` key as unknown rather than disabled. On GitLab, Dependency Scanning requires Ultimate, and a Free project reaches the same outcome through a job running an advisory-database scanner. A vulnerability the change introduces blocks its merge; the repository-wide scan reports rather than blocks, because an advisory published against a dependency with no fix is not something a contributor can resolve.
|
|
110
|
+
- R-SEC-12 n/a One principal, svyatov, holds all push/maintain/admin access; collaborators and teams APIs confirm no second reviewer can merge.
|
|
111
|
+
- R-SEC-13 fail GitHub rulesets API returns []; no rule restricts release-tag creation, updates, or deletion.
|
|
112
|
+
Fixed by: oss-harden
|
|
113
|
+
Check: on GitHub, a repository ruleset targeting `refs/tags/*` blocks tag update and tag deletion and restricts tag creation to named principals, read back with `gh api repos/{owner}/{repo}/rulesets` rather than inferred from the write; on GitLab, protected tags cover the release tag pattern with a create access level set and no role permitted to force-update it, read back with `GET /projects/:id/protected_tags`. This rule covers a git tag only. A container registry tag is mutable by design, lives outside the forge, and is reachable by no forge control, so the immutable identity of a published image is its digest rather than its tag.
|
|
114
|
+
- R-SEC-14 n/a No dependency updater configuration exists; that gap is scored under R-SEC-03.
|
|
115
|
+
- R-SEC-15 n/a RubyGems is the only present package ecosystem; the standard explicitly exempts its native-extension installation behavior.
|
|
116
|
+
- R-PUB-01 pass rubygems, shipped: release.yml:3-5,82 publishes on v* tag pushes; no local script or Rake task publishes gems.
|
|
117
|
+
- R-PUB-02 pass rubygems, shipped: release.yml:64-78 grants id-token: write and uses configure-rubygems-credentials; release run 40668522014 completed authentication and publication successfully.
|
|
118
|
+
- R-PUB-03 unknown rubygems, shipped: RubyGems serves one 0.1.0 Sigstore bundle; artifact digest and ECDSA signature match, but the Fulcio trust chain and transparency proof were not verified.
|
|
119
|
+
Fixed by: oss-publish
|
|
120
|
+
Check: the exact published artifact has verifiable provenance tied to the expected repository and workflow. Prefer a registry-served record: verify npm provenance for an installed exact package version, PyPI provenance through its Integrity API, or a RubyGems attestation through its attestation API. Where a registry cannot serve provenance, verify a forge attestation for the exact published artifact and report the registry limitation; OIDC authentication alone is not provenance.
|
|
121
|
+
- R-PUB-04 pass rubygems, shipped: release.yml:63 gates publishing through release; the environment API confirms required reviewer svyatov before public upload.
|
|
122
|
+
- R-PUB-05 n/a The newest GitHub release v0.1.0 has assets: []; it carries only forge-generated source archives, not attached built assets.
|
|
123
|
+
- R-PUB-06 n/a The newest GitHub release v0.1.0 has assets: []; it carries only forge-generated source archives, not attached built assets.
|
|
124
|
+
- R-PUB-07 n/a RubyGems is registry-push, with no forge-updated registry entry; no tag-published ecosystem is shipped.
|
|
125
|
+
- R-CHG-01 fail CHANGELOG.md:1-7 follows Keep a Changelog structure but includes no pinned convention link or other declared changelog convention.
|
|
126
|
+
Fixed by: oss-changelog
|
|
127
|
+
Check: each release unit has a discoverable changelog following the project's declared convention. For Keep a Changelog 2.0.0, it opens with `# Changelog` and a pinned convention link, carries `## [Unreleased]`, lists dated releases newest first, groups notable changes under the six standard types, marks incompatible entries `**Breaking:**`, and resolves version headings to tag or comparison links.
|
|
128
|
+
- R-CHG-02 fail README.md, CONTRIBUTING.md, and CHANGELOG.md declare neither the public API covered by Semantic Versioning nor a versioning policy.
|
|
129
|
+
Fixed by: oss-changelog
|
|
130
|
+
Check: the project declares the public API covered by Semantic Versioning; versions use valid SemVer syntax, allowing a `v` prefix only on tag names; stable releases increment MAJOR for incompatible public API changes, MINOR for compatible additions and deprecations, and PATCH for compatible fixes. During `0.y.z` initial development, this standard uses MINOR for incompatible changes rather than forcing `1.0.0`; SemVer itself permits anything to change before 1.0.0.
|
|
131
|
+
- R-CHG-03 pass v0.1.0, lib/crap4ruby/version.rb:4, the gemspec's VERSION reference, Gemfile.lock, CHANGELOG.md:5, and RubyGems metadata agree on 0.1.0.
|
|
132
|
+
- R-CHG-04 pass gh release view v0.1.0 returns the complete Added and Changed entries from CHANGELOG.md:7-17 with no conflicting text.
|
|
133
|
+
- R-CHG-05 n/a The only release is 0.1.0 and no released public item has been removed; the changelog has no Removed entries.
|
|
134
|
+
- R-CHG-06 unknown rubygems, shipped: fixed Dependabot alerts cannot be read because alerts are disabled (403); no scanner history establishes the complete set of resolved runtime advisories.
|
|
135
|
+
Fixed by: oss-changelog
|
|
136
|
+
Check: the changelog entry for each release that resolved a publicly known vulnerability in the shipped code or in a run-time dependency names every such vulnerability under Security, each by an identifier from a published advisory. A development-only dependency is not a run-time vulnerability. Where the project ships a built artifact, its build toolchain is one, because a compromised bundler or code generator injects into the thing users install; where the project ships only source, a build-only dependency stays outside. On GitHub, `gh api repos/{owner}/{repo}/dependabot/alerts?state=fixed` reports which advisories were resolved and when, which is what locates them in a release; on GitLab, the project's vulnerability report carries the same and requires Ultimate, so a Free project resolves it from the advisory identifiers its scanner job recorded. A project that has fixed no publicly known run-time vulnerability falls outside this rule rather than satisfying it with nothing to check.
|
|
137
|
+
- R-CHG-07 n/a RubyGems package identity does not encode the major version.
|
|
138
|
+
- R-SKL-01 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
139
|
+
- R-SKL-02 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
140
|
+
- R-SKL-03 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
141
|
+
- R-SKL-04 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
142
|
+
- R-SKL-05 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
143
|
+
- R-SKL-06 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
144
|
+
- R-SKL-07 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
145
|
+
|
|
146
|
+
## Execution order
|
|
147
|
+
|
|
148
|
+
Each group starts in a fresh session, reads this report, and uses one pull request as its checkpoint.
|
|
149
|
+
Merge the checkpoint before starting the next group.
|
|
150
|
+
No group is authorized by this audit to publish a release.
|
|
151
|
+
|
|
152
|
+
1. `/oss-kit:oss-community`: 5 rules (R-COM-02, R-COM-03, R-COM-04, R-COM-05, R-COM-08), branch `docs/community-files`. Restore the missing policy, contribution, template, and governance documents.
|
|
153
|
+
2. `/oss-kit:oss-harden`: 6 rules (R-SEC-03, R-SEC-04, R-SEC-08, R-SEC-09, R-SEC-11, R-SEC-13), branch `ci/harden`. Add dependency and analysis checks first; require them in branch protection afterward, then protect release tags.
|
|
154
|
+
3. `/oss-kit:oss-readme`: 4 rules (R-DOC-02, R-DOC-07, R-DOC-09, R-DOC-10), branch `docs/readme-structure`. Document output, snippet destinations, maintenance status, and an evidenced distinction.
|
|
155
|
+
4. `/oss-kit:oss-changelog`: 3 rules (R-CHG-01, R-CHG-02, R-CHG-06), branch `docs/versioning-policy`. Declare the changelog convention and public API; investigate historical advisories before scoring R-CHG-06.
|
|
156
|
+
5. `/oss-kit:oss-publish`: 1 rule (R-PUB-03), branch `ci/release-verification`. Run complete Sigstore verification for R-PUB-03. Any necessary release is a separately approved final action after all other groups.
|
|
157
|
+
|
|
158
|
+
R-SEC-05 already passes. If a later approved release is required, verify its new tag again in the final release group.
|
|
159
|
+
After the fix groups, run `/oss-kit:oss-audit` in another fresh session. Preserve this baseline and compare every verdict with the new report.
|
data/oss-audit-report.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Open source audit report
|
|
2
|
+
|
|
3
|
+
Generated by oss-audit verification on 2026-10-28 for commit `c9312afc734b54a1d4851057d6c5f67979e9681d`.
|
|
4
|
+
This report is untracked by default; the maintainer may commit or delete it.
|
|
5
|
+
The original audit is preserved in [oss-audit-report.before.md](oss-audit-report.before.md).
|
|
6
|
+
|
|
7
|
+
## Repository
|
|
8
|
+
|
|
9
|
+
GitHub: [svyatov/crap4ruby](https://github.com/svyatov/crap4ruby). Default branch: `main`.
|
|
10
|
+
Audited 42 applicable rules: 42 pass, 0 fail, 0 unknown; 19 not applicable. All 61 rules are accounted for; none are retired.
|
|
11
|
+
Standard SHA256: `d2673e5b637124664c83afbf6820a78a0014dc735f649450642ddd0ddf0ffa5a`.
|
|
12
|
+
|
|
13
|
+
Not applicable: seven agent-skill rules; two single-maintainer rules; two forge-release-asset rules; one each for a separate question channel, dependency caches, GitLab, RubyGems install-script controls, tag-published registry updates, public API removals, fixed runtime advisories, and major-version package identities.
|
|
14
|
+
|
|
15
|
+
Shipped ecosystems: RubyGems, `crap4ruby`, registry-push track. Present ecosystems: RubyGems through the root gemspec/Gemfile and `gemfiles/release/Gemfile`.
|
|
16
|
+
Committed locks: `Gemfile.lock` and `gemfiles/release/Gemfile.lock`. No other roster manifest is present.
|
|
17
|
+
Built forge-release assets: none. The published artifact is the RubyGems gem; GitHub v0.1.0 has no attached assets.
|
|
18
|
+
No container push appears in the workflows. The separate account package inventory remains unavailable without read:packages scope.
|
|
19
|
+
|
|
20
|
+
CI files: `.github/workflows/ci.yml`, `codeql.yml`, `dependency-review.yml`, and `release.yml`.
|
|
21
|
+
The release still uses a signed v* tag, test/build separation, the human-approved `release` environment, and RubyGems OIDC.
|
|
22
|
+
No release was published during this work. The latest released version remains 0.1.0.
|
|
23
|
+
|
|
24
|
+
Four sequential pull requests were squash-merged: [community](https://github.com/svyatov/crap4ruby/pull/1), [hardening](https://github.com/svyatov/crap4ruby/pull/2), [README](https://github.com/svyatov/crap4ruby/pull/3), and [versioning](https://github.com/svyatov/crap4ruby/pull/4).
|
|
25
|
+
The fifth group verified the existing artifact and needed no repository change or pull request.
|
|
26
|
+
Every pull request passed eight required checks after the security controls were introduced; the first passed its three existing Ruby checks.
|
|
27
|
+
|
|
28
|
+
Verification used the collector, source inspection, live GitHub settings, the rendered bug form, the Ruby 3.3.11 installation example, and the installed Sigstore verifier.
|
|
29
|
+
The five groups and this follow-up verification ran in the same working session; this is not an independent fresh-session audit.
|
|
30
|
+
The published gem SHA256 is `29cedba469c21114a8c6c2898303030990ff0f367250e620f2a42aaf57d76096`.
|
|
31
|
+
The verifier accepted its exact workflow identity and issuer using updated Sigstore trust data.
|
|
32
|
+
|
|
33
|
+
Against the baseline: all 17 failures became passes; provenance changed from unknown to pass; historical advisories changed from unknown to not applicable.
|
|
34
|
+
The updater-delay rule changed from not applicable to pass. No previous pass became a failure or unknown.
|
|
35
|
+
|
|
36
|
+
## Prerequisites
|
|
37
|
+
|
|
38
|
+
None remain for the audited rules. The maintainer confirmed the reporting contact, response window, contribution criteria, governance facts, and README fact selection.
|
|
39
|
+
The global handrail workflow rule required confirmation; the maintainer approved all four workflow changes before they were committed.
|
|
40
|
+
No new credentials, registry publisher, paid feature, or release approval is required for these completed changes.
|
|
41
|
+
|
|
42
|
+
## Verdicts
|
|
43
|
+
|
|
44
|
+
- R-DOC-01 pass README.md:3 states the tool and Ruby-method scope in one sentence before headings.
|
|
45
|
+
- R-DOC-02 pass README.md:15 installs the gem before the sample; the next shell block includes observed complexity and snapshot results.
|
|
46
|
+
- R-DOC-03 pass README.md links existing LICENSE, CHANGELOG.md, and CONTRIBUTING.md files.
|
|
47
|
+
- R-DOC-04 pass README.md claims match the CLI help, source, manifests, and CI; the single-file example was executed with the published gem on Ruby 3.3.11.
|
|
48
|
+
- R-DOC-05 pass The prose scanner passes README.md, CONTRIBUTING.md, SECURITY.md, VERSIONING.md, and CHANGELOG.md; the seven preserved Contributor Covenant headings use the explicit attributed-code-of-conduct exception.
|
|
49
|
+
- R-DOC-06 pass README.md names Ruby 3.3+ and tested CRuby 3.3, 3.4, and 4.0; these match the gemspec and CI matrix.
|
|
50
|
+
- R-DOC-07 pass All README fences have language tags; Gemfile, test helper, scoring engine, and .metrics/crap.edn are named before the corresponding non-shell blocks.
|
|
51
|
+
- R-DOC-08 pass README.md:198 links public GitHub issues for both questions and defects.
|
|
52
|
+
- R-DOC-09 pass README.md:199 states the maintainer and links the latest-release security support policy.
|
|
53
|
+
- R-DOC-10 pass README.md:6 distinguishes coverage-aware ranking from complexity alone; lib/crap4ruby/score.rb implements that distinction.
|
|
54
|
+
- R-COM-01 pass LICENSE:1 contains MIT; crap4ruby.gemspec:11 declares MIT, as does RubyGems 0.1.0 metadata.
|
|
55
|
+
- R-COM-02 pass CONTRIBUTING.md gives setup and test commands, fork-to-main submission steps, tests for new behavior, and a working link to its acceptance criteria.
|
|
56
|
+
- R-COM-03 pass CODE_OF_CONDUCT.md contains Contributor Covenant 3.0, its attribution, and the confirmed monitored reporting contact with no placeholder fields.
|
|
57
|
+
- R-COM-04 pass SECURITY.md names the maintainer-confirmed private email and commits to a response within 14 days.
|
|
58
|
+
- R-COM-05 pass .github/ISSUE_TEMPLATE/01-bug.yml and .github/pull_request_template.md are on main; the live GitHub bug form renders all fields and required markers.
|
|
59
|
+
- R-COM-06 n/a One principal, svyatov, holds all push/maintain/admin access; the collaborators API lists only that account and the teams API returns [].
|
|
60
|
+
- R-COM-07 pass GitHub repository API returns a description, four topics, and homepage https://rubygems.org/gems/crap4ruby.
|
|
61
|
+
- R-COM-08 pass CONTRIBUTING.md states that Leonid Svyatov decides merges and releases and that no succession arrangement exists; both facts were confirmed by the maintainer.
|
|
62
|
+
- R-COM-09 n/a No separate public question channel is advertised; GitHub Discussions is disabled and README.md:185 routes questions to issues.
|
|
63
|
+
- R-CI-01 pass .github/workflows/ci.yml:3-6 runs on main pushes and unfiltered pull_request events; main is the API-reported default branch.
|
|
64
|
+
- R-CI-02 pass CI runs bundle exec rake and the frozen release-tool smoke check; CONTRIBUTING.md uses the same test entry point. The lock task regenerates dependencies and is not a CI check.
|
|
65
|
+
- R-CI-03 pass rubygems, present: crap4ruby.gemspec:20 requires Ruby >=3.3; ci.yml:22 and release.yml:22 cover maintained Ruby 3.3, 3.4, and 4.0.
|
|
66
|
+
- R-CI-04 n/a Neither workflow defines a dependency cache; every ruby/setup-ruby invocation sets bundler-cache: false.
|
|
67
|
+
- R-CI-05 pass All eight workflow job definitions set timeouts; CI, CodeQL, and Dependency review cancel superseded runs for the same ref.
|
|
68
|
+
- R-CI-06 pass test/*_test.rb contains automated tests; Rakefile:9-12 discovers them and default:crap invokes the suite.
|
|
69
|
+
- R-SEC-01 pass All 20 external uses entries across four workflow files pin full 40-character SHAs; no composite action is shipped.
|
|
70
|
+
- R-SEC-02 pass All four workflows default to contents: read; CodeQL grants security-events: write per analysis job and release jobs retain their narrow exceptions.
|
|
71
|
+
- R-SEC-03 pass rubygems, present: .github/dependabot.yml covers root and gemfiles/release Bundler manifests plus github-actions; GitHub reports successful Dependabot configuration checks.
|
|
72
|
+
- R-SEC-04 pass GitHub ruleset 26031925 is active on the default branch, requires pull requests and eight app-bound checks, blocks force pushes and deletion, and has no bypass actors.
|
|
73
|
+
- R-SEC-05 pass After git fetch --tags, v0.1.0 is an annotated tag; git tag -v succeeds against svyatov's API-published SSH signing key and tagger email leonid@svyatov.com.
|
|
74
|
+
- R-SEC-06 n/a The repository uses GitHub, not GitLab.
|
|
75
|
+
- R-SEC-07 pass The four workflows contain no user-controlled event expressions in shell scripts and no privileged pull_request_target/workflow_run execution.
|
|
76
|
+
- R-SEC-08 pass rubygems, present: Gemfile.lock and gemfiles/release/Gemfile.lock are committed with SHA256 checksums and Linux resolution; all dependency installs use BUNDLE_FROZEN=true.
|
|
77
|
+
- R-SEC-09 pass CodeQL advanced setup scans Ruby and Actions on unfiltered pull requests; both analysis checks and the CodeQL result are required, with a code_scanning rule for high-severity alerts.
|
|
78
|
+
- R-SEC-10 pass GitHub repository API security_and_analysis reports secret_scanning and secret_scanning_push_protection enabled.
|
|
79
|
+
- R-SEC-11 pass rubygems, present: vulnerability-alerts returns 204; the SBOM contains all 17 resolved gems across both locks, and required Dependency review blocks introduced vulnerabilities in all dependency scopes.
|
|
80
|
+
- R-SEC-12 n/a One principal, svyatov, holds all push/maintain/admin access; collaborators and teams APIs confirm no second reviewer can merge.
|
|
81
|
+
- R-SEC-13 pass Active tag rulesets 26031926 and 26031929 block v* updates and deletion without bypass and restrict creation to repository administrators; svyatov is the only administrator.
|
|
82
|
+
- R-SEC-14 pass rubygems and github-actions: .github/dependabot.yml explicitly applies a three-day cooldown with no exclusions; security updates remain exempt.
|
|
83
|
+
- R-SEC-15 n/a RubyGems is the only present package ecosystem; the standard explicitly exempts its native-extension installation behavior.
|
|
84
|
+
- R-PUB-01 pass rubygems, shipped: release.yml publishes only after a v* tag, tests, build, and approval; no local task publishes a package.
|
|
85
|
+
- R-PUB-02 pass rubygems, shipped: the publish job retains id-token: write and the pinned RubyGems OIDC action; successful release run 40668522014 proves the configured flow.
|
|
86
|
+
- R-PUB-03 pass rubygems, shipped: sigstore-cli 0.2.3 refreshed its TUF trust data and returned OK for the downloaded 0.1.0 gem with the expected release.yml@refs/tags/v0.1.0 identity and GitHub OIDC issuer.
|
|
87
|
+
- R-PUB-04 pass rubygems, shipped: the release environment API confirms human reviewer svyatov and a v* tag policy; publish remains behind that environment.
|
|
88
|
+
- R-PUB-05 n/a The newest GitHub release v0.1.0 has assets: []; it carries only forge-generated source archives, not attached built assets.
|
|
89
|
+
- R-PUB-06 n/a The newest GitHub release v0.1.0 has assets: []; it carries only forge-generated source archives, not attached built assets.
|
|
90
|
+
- R-PUB-07 n/a RubyGems is registry-push, with no forge-updated registry entry; no tag-published ecosystem is shipped.
|
|
91
|
+
- R-CHG-01 pass CHANGELOG.md pins Keep a Changelog 2.0.0, retains Unreleased and dated release headings with working version links, and preserves the released entries.
|
|
92
|
+
- R-CHG-02 pass VERSIONING.md declares the CLI, scoring, snapshot, and Ruby-version contracts, with stable SemVer increments and explicit pre-1.0 rules.
|
|
93
|
+
- R-CHG-03 pass The newest release remains 0.1.0 in its tag, version.rb, gemspec, root lockfile, changelog, and registry metadata; no release bump was made.
|
|
94
|
+
- R-CHG-04 pass script/release_notes.rb v0.1.0 still produces the exact published GitHub release body after the changelog edits.
|
|
95
|
+
- R-CHG-05 n/a The only release is 0.1.0 and no released public item has been removed; the changelog has no Removed entries.
|
|
96
|
+
- R-CHG-06 n/a The now-enabled fixed-advisory API returns []; the single release and dependency history contain no known runtime vulnerability fix.
|
|
97
|
+
- R-CHG-07 n/a RubyGems package identity does not encode the major version.
|
|
98
|
+
- R-SKL-01 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
99
|
+
- R-SKL-02 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
100
|
+
- R-SKL-03 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
101
|
+
- R-SKL-04 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
102
|
+
- R-SKL-05 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
103
|
+
- R-SKL-06 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
104
|
+
- R-SKL-07 n/a The checkout contains no SKILL.md, including through symlinked directories; the agent-skills area is outside scope.
|
|
105
|
+
|
|
106
|
+
## Execution order
|
|
107
|
+
|
|
108
|
+
No fix groups remain for this audit. The completed order was oss-community, oss-harden, oss-readme, oss-changelog, then oss-publish verification.
|
|
109
|
+
A later independent audit should start in a fresh session and compare against this report and the preserved baseline.
|
|
110
|
+
Any future release remains a separate, human-approved action.
|