coderifts 6.0.2 → 6.1.0
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.
- package/CHANGELOG.md +6 -0
- package/dist/cli.js +89 -14
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 6.1.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **enforce --check now reads GitHub rulesets.** The evaluator existed with zero production call sites: a ruleset-governed repository reported required_check NOT_VERIFIED while its merges were gated. Rulesets are readable where classic protection is not (200 vs 401), so required_check can now reach VERIFIED on an unreadable classic response.
|
|
8
|
+
|
|
3
9
|
## 6.0.2
|
|
4
10
|
|
|
5
11
|
### Fixed
|
package/dist/cli.js
CHANGED
|
@@ -3028,7 +3028,7 @@ var require_package = __commonJS({
|
|
|
3028
3028
|
"package.json"(exports2, module2) {
|
|
3029
3029
|
module2.exports = {
|
|
3030
3030
|
name: "coderifts",
|
|
3031
|
-
version: "6.0
|
|
3031
|
+
version: "6.1.0",
|
|
3032
3032
|
description: "Detect breaking API changes from the command line. Works locally or with the CodeRifts cloud API.",
|
|
3033
3033
|
author: "CodeRifts <hello@coderifts.com>",
|
|
3034
3034
|
license: "MIT",
|
|
@@ -254196,7 +254196,9 @@ var require_github_enforcement = __commonJS({
|
|
|
254196
254196
|
});
|
|
254197
254197
|
function verifiedRequiresBinding(layer2) {
|
|
254198
254198
|
if (!layer2 || layer2.status !== STATUS.VERIFIED) return true;
|
|
254199
|
-
if (layer2.id === "required_check")
|
|
254199
|
+
if (layer2.id === "required_check") {
|
|
254200
|
+
return layer2.reason_code === REASON.ISSUER_BOUND || layer2.reason_code === REASON.RULESET_ISSUER_BOUND;
|
|
254201
|
+
}
|
|
254200
254202
|
if (layer2.id === "workflow_contract_gate" || layer2.id === "workflow_deploy_gate") {
|
|
254201
254203
|
return layer2.reason_code === REASON.ACTIVE_JOB;
|
|
254202
254204
|
}
|
|
@@ -254648,15 +254650,48 @@ var require_github_enforcement = __commonJS({
|
|
|
254648
254650
|
environmentRead,
|
|
254649
254651
|
environmentName,
|
|
254650
254652
|
local,
|
|
254651
|
-
auth
|
|
254653
|
+
auth,
|
|
254654
|
+
rulesetsRead = null,
|
|
254655
|
+
branch = null
|
|
254652
254656
|
} = {}) {
|
|
254653
254657
|
const layers = [];
|
|
254654
254658
|
const localFiles = local || { contractGateFiles: [], deployGateFiles: [] };
|
|
254659
|
+
const rulesetVerdict = evaluateRuleset(rulesetsRead, branch);
|
|
254660
|
+
const rulesetUpgrade = (classicLayerFields) => {
|
|
254661
|
+
if (rulesetVerdict.status !== STATUS.VERIFIED) return null;
|
|
254662
|
+
return {
|
|
254663
|
+
status: STATUS.VERIFIED,
|
|
254664
|
+
reason: `${rulesetVerdict.reason} Classic branch protection did not establish this: ${classicLayerFields.reason}`,
|
|
254665
|
+
reason_code: rulesetVerdict.reason_code,
|
|
254666
|
+
endpoint: "GET /repos/{owner}/{repo}/rulesets",
|
|
254667
|
+
app_id: rulesetVerdict.app_id,
|
|
254668
|
+
ruleset_names: rulesetVerdict.ruleset_names,
|
|
254669
|
+
surfaces_inspected: ["classic_branch_protection", "rulesets"]
|
|
254670
|
+
};
|
|
254671
|
+
};
|
|
254672
|
+
const pushRequiredCheck = (classicFields) => {
|
|
254673
|
+
const up = rulesetUpgrade(classicFields);
|
|
254674
|
+
layers.push(layer("required_check", up ? up.status : classicFields.status, {
|
|
254675
|
+
...up || classicFields,
|
|
254676
|
+
ruleset_status: rulesetVerdict.status,
|
|
254677
|
+
ruleset_reason_code: rulesetVerdict.reason_code,
|
|
254678
|
+
surfaces_inspected: [
|
|
254679
|
+
"classic_branch_protection",
|
|
254680
|
+
rulesetsRead == null ? "rulesets:unread" : "rulesets"
|
|
254681
|
+
]
|
|
254682
|
+
}));
|
|
254683
|
+
};
|
|
254655
254684
|
const noAuth = !auth || auth.source === "none" || !auth.ok;
|
|
254656
254685
|
const prot = protectionRead || null;
|
|
254657
254686
|
if (noAuth && !prot) {
|
|
254658
254687
|
const why = auth && auth.reason || NO_TOKEN_INSTRUCTION;
|
|
254659
|
-
|
|
254688
|
+
pushRequiredCheck({
|
|
254689
|
+
status: STATUS.UNVERIFIABLE,
|
|
254690
|
+
reason: why,
|
|
254691
|
+
endpoint: null,
|
|
254692
|
+
missing_scope: APP_INSTALLATION.missing_scope
|
|
254693
|
+
});
|
|
254694
|
+
for (const id of ["enforce_admins", "strict_status_checks"]) {
|
|
254660
254695
|
layers.push(layer(id, STATUS.UNVERIFIABLE, {
|
|
254661
254696
|
reason: why,
|
|
254662
254697
|
endpoint: null,
|
|
@@ -254665,13 +254700,21 @@ var require_github_enforcement = __commonJS({
|
|
|
254665
254700
|
}
|
|
254666
254701
|
} else if (!prot) {
|
|
254667
254702
|
const why = "branch protection was not queried";
|
|
254668
|
-
|
|
254703
|
+
pushRequiredCheck({ status: STATUS.UNVERIFIABLE, reason: why, endpoint: null });
|
|
254704
|
+
for (const id of ["enforce_admins", "strict_status_checks"]) {
|
|
254669
254705
|
layers.push(layer(id, STATUS.UNVERIFIABLE, { reason: why, endpoint: null }));
|
|
254670
254706
|
}
|
|
254671
254707
|
} else if (prot.status === 403) {
|
|
254672
254708
|
const why = "GitHub HTTP 403 \u2014 missing scope administration:read (branch protection). Grant Administration: Read on the token, or use a repo-admin token.";
|
|
254673
254709
|
const endpoint = prot.endpoint ? `GET ${prot.endpoint}` : null;
|
|
254674
|
-
|
|
254710
|
+
pushRequiredCheck({
|
|
254711
|
+
status: STATUS.UNVERIFIABLE,
|
|
254712
|
+
reason: why,
|
|
254713
|
+
endpoint,
|
|
254714
|
+
missing_scope: "administration:read",
|
|
254715
|
+
http_status: 403
|
|
254716
|
+
});
|
|
254717
|
+
for (const id of ["enforce_admins", "strict_status_checks"]) {
|
|
254675
254718
|
layers.push(layer(id, STATUS.UNVERIFIABLE, {
|
|
254676
254719
|
reason: why,
|
|
254677
254720
|
endpoint,
|
|
@@ -254681,11 +254724,12 @@ var require_github_enforcement = __commonJS({
|
|
|
254681
254724
|
}
|
|
254682
254725
|
} else if (prot.status === 404) {
|
|
254683
254726
|
const endpoint = prot.endpoint ? `GET ${prot.endpoint}` : null;
|
|
254684
|
-
|
|
254727
|
+
pushRequiredCheck({
|
|
254728
|
+
status: STATUS.NOT_VERIFIED,
|
|
254685
254729
|
reason: `classic branch protection is absent \u2014 required check "${CHECK_NAME}" is not required`,
|
|
254686
254730
|
endpoint,
|
|
254687
254731
|
http_status: 404
|
|
254688
|
-
})
|
|
254732
|
+
});
|
|
254689
254733
|
layers.push(layer("enforce_admins", STATUS.NOT_VERIFIED, {
|
|
254690
254734
|
reason: "classic branch protection is absent \u2014 administrators are unrestricted",
|
|
254691
254735
|
endpoint,
|
|
@@ -254699,7 +254743,13 @@ var require_github_enforcement = __commonJS({
|
|
|
254699
254743
|
} else if (prot.status >= 400 || prot.status === 0) {
|
|
254700
254744
|
const endpoint = prot.endpoint ? `GET ${prot.endpoint}` : null;
|
|
254701
254745
|
const why = `GitHub HTTP ${prot.status || 0} reading branch protection \u2014 cannot verify`;
|
|
254702
|
-
|
|
254746
|
+
pushRequiredCheck({
|
|
254747
|
+
status: STATUS.UNVERIFIABLE,
|
|
254748
|
+
reason: redactSecrets(why),
|
|
254749
|
+
endpoint,
|
|
254750
|
+
http_status: prot.status
|
|
254751
|
+
});
|
|
254752
|
+
for (const id of ["enforce_admins", "strict_status_checks"]) {
|
|
254703
254753
|
layers.push(layer(id, STATUS.UNVERIFIABLE, {
|
|
254704
254754
|
reason: redactSecrets(why),
|
|
254705
254755
|
endpoint,
|
|
@@ -254710,14 +254760,15 @@ var require_github_enforcement = __commonJS({
|
|
|
254710
254760
|
const protection = prot.body && typeof prot.body === "object" ? prot.body : {};
|
|
254711
254761
|
const endpoint = prot.endpoint ? `GET ${prot.endpoint}` : null;
|
|
254712
254762
|
const required = evaluateRequiredCheck(protection);
|
|
254713
|
-
|
|
254763
|
+
pushRequiredCheck({
|
|
254764
|
+
status: required.status,
|
|
254714
254765
|
reason: required.reason,
|
|
254715
254766
|
reason_code: required.reason_code,
|
|
254716
254767
|
endpoint,
|
|
254717
254768
|
required_contexts: required.required_contexts,
|
|
254718
254769
|
app_id: required.app_id,
|
|
254719
254770
|
...required.expected_app_id != null ? { expected_app_id: required.expected_app_id } : {}
|
|
254720
|
-
})
|
|
254771
|
+
});
|
|
254721
254772
|
const admins = enforceAdminsEnabled(protection);
|
|
254722
254773
|
layers.push(layer("enforce_admins", admins ? STATUS.VERIFIED : STATUS.NOT_VERIFIED, {
|
|
254723
254774
|
reason: admins ? "enforce_admins.enabled is true \u2014 administrators cannot bypass required checks" : "enforce_admins.enabled is false \u2014 repository administrators can bypass required checks",
|
|
@@ -254885,11 +254936,27 @@ var require_github_enforcement = __commonJS({
|
|
|
254885
254936
|
const protPath = resolvedBranch ? `repos/${owner}/${repo}/branches/${encodeURIComponent(resolvedBranch)}/protection` : protectionPath;
|
|
254886
254937
|
const protectionRead = client && protPath ? await client.get(protPath) : null;
|
|
254887
254938
|
const environmentRead = client && envPath ? await client.get(envPath) : null;
|
|
254939
|
+
let rulesetsRead = null;
|
|
254940
|
+
if (client) {
|
|
254941
|
+
const list = await client.get(`repos/${owner}/${repo}/rulesets`);
|
|
254942
|
+
if (list && list.status === 200 && Array.isArray(list.body)) {
|
|
254943
|
+
const detailed = [];
|
|
254944
|
+
let anyDetailFailed = false;
|
|
254945
|
+
for (const rs of list.body) {
|
|
254946
|
+
if (!rs || rs.id == null) continue;
|
|
254947
|
+
const one = await client.get(`repos/${owner}/${repo}/rulesets/${rs.id}`);
|
|
254948
|
+
if (one && one.status === 200 && one.body) detailed.push(one.body);
|
|
254949
|
+
else anyDetailFailed = true;
|
|
254950
|
+
}
|
|
254951
|
+
rulesetsRead = anyDetailFailed ? null : detailed;
|
|
254952
|
+
}
|
|
254953
|
+
}
|
|
254888
254954
|
return {
|
|
254889
254955
|
branch: resolvedBranch,
|
|
254890
254956
|
repoRead,
|
|
254891
254957
|
protectionRead,
|
|
254892
|
-
environmentRead
|
|
254958
|
+
environmentRead,
|
|
254959
|
+
rulesetsRead
|
|
254893
254960
|
};
|
|
254894
254961
|
}
|
|
254895
254962
|
module2.exports = {
|
|
@@ -255853,6 +255920,7 @@ var require_enforce_check = __commonJS({
|
|
|
255853
255920
|
let branch = options.branch ? String(options.branch).trim() : null;
|
|
255854
255921
|
let protectionRead = null;
|
|
255855
255922
|
let environmentRead = null;
|
|
255923
|
+
let rulesetsRead = null;
|
|
255856
255924
|
if (auth.ok) {
|
|
255857
255925
|
const client = deps.githubClient || createGitHubHttpClient({
|
|
255858
255926
|
token: auth.token,
|
|
@@ -255868,13 +255936,18 @@ var require_enforce_check = __commonJS({
|
|
|
255868
255936
|
branch = queried.branch || branch;
|
|
255869
255937
|
protectionRead = queried.protectionRead;
|
|
255870
255938
|
environmentRead = queried.environmentRead;
|
|
255939
|
+
rulesetsRead = queried.rulesetsRead;
|
|
255871
255940
|
}
|
|
255872
255941
|
const evaluated = evaluateLayers({
|
|
255873
255942
|
protectionRead,
|
|
255874
255943
|
environmentRead,
|
|
255875
255944
|
environmentName,
|
|
255876
255945
|
local,
|
|
255877
|
-
auth
|
|
255946
|
+
auth,
|
|
255947
|
+
// roadmap 1052a — the ruleset surface, which reads without a credential and can establish
|
|
255948
|
+
// required_check where classic branch protection is unreadable.
|
|
255949
|
+
rulesetsRead,
|
|
255950
|
+
branch
|
|
255878
255951
|
});
|
|
255879
255952
|
const report = {
|
|
255880
255953
|
command: "enforce --check",
|
|
@@ -255894,7 +255967,9 @@ var require_enforce_check = __commonJS({
|
|
|
255894
255967
|
// response or to our grading of it.
|
|
255895
255968
|
responses: {
|
|
255896
255969
|
protection: responseDigest(protectionRead),
|
|
255897
|
-
environment: responseDigest(environmentRead)
|
|
255970
|
+
environment: responseDigest(environmentRead),
|
|
255971
|
+
// null here means NOT READ, which is different from an empty ruleset list.
|
|
255972
|
+
rulesets: rulesetsRead == null ? null : { count: rulesetsRead.length }
|
|
255898
255973
|
},
|
|
255899
255974
|
...evaluated
|
|
255900
255975
|
};
|
package/package.json
CHANGED