@synkro-sh/cli 1.3.25 → 1.3.26
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/dist/bootstrap.js +72 -15
- package/dist/bootstrap.js.map +1 -1
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -2508,7 +2508,7 @@ jobs:
|
|
|
2508
2508
|
scan:
|
|
2509
2509
|
runs-on: ubuntu-latest
|
|
2510
2510
|
permissions:
|
|
2511
|
-
contents:
|
|
2511
|
+
contents: write
|
|
2512
2512
|
pull-requests: write
|
|
2513
2513
|
checks: write
|
|
2514
2514
|
steps:
|
|
@@ -3333,7 +3333,7 @@ function writeConfigEnv(opts) {
|
|
|
3333
3333
|
`SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
|
|
3334
3334
|
`SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
|
|
3335
3335
|
`SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
|
|
3336
|
-
`SYNKRO_VERSION=${shellQuoteSingle("1.3.
|
|
3336
|
+
`SYNKRO_VERSION=${shellQuoteSingle("1.3.26")}`
|
|
3337
3337
|
];
|
|
3338
3338
|
if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
|
|
3339
3339
|
if (safeOrgId) lines.push(`SYNKRO_ORG_ID=${shellQuoteSingle(safeOrgId)}`);
|
|
@@ -4374,6 +4374,60 @@ function ghJson(args2) {
|
|
|
4374
4374
|
});
|
|
4375
4375
|
return JSON.parse(out);
|
|
4376
4376
|
}
|
|
4377
|
+
async function ensureOpenPr(repo, prNumber, sha) {
|
|
4378
|
+
let pr;
|
|
4379
|
+
try {
|
|
4380
|
+
pr = ghJson(["api", `/repos/${repo}/pulls/${prNumber}`]);
|
|
4381
|
+
} catch {
|
|
4382
|
+
return { prNumber, sha, branched: false };
|
|
4383
|
+
}
|
|
4384
|
+
if (pr.state === "open") {
|
|
4385
|
+
return { prNumber, sha, branched: false };
|
|
4386
|
+
}
|
|
4387
|
+
if (pr.merged) {
|
|
4388
|
+
console.log(`PR #${prNumber} is merged. Scanning original diff and posting findings there.
|
|
4389
|
+
`);
|
|
4390
|
+
return { prNumber, sha: pr.head.sha, branched: false };
|
|
4391
|
+
}
|
|
4392
|
+
console.log(`PR #${prNumber} is closed. Creating a scan branch...
|
|
4393
|
+
`);
|
|
4394
|
+
const scanBranch = `synkro/scan-${pr.head.ref}-${Date.now()}`;
|
|
4395
|
+
try {
|
|
4396
|
+
execSync5(`gh api -X POST /repos/${repo}/git/refs --input -`, {
|
|
4397
|
+
encoding: "utf-8",
|
|
4398
|
+
input: JSON.stringify({ ref: `refs/heads/${scanBranch}`, sha: pr.head.sha }),
|
|
4399
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4400
|
+
});
|
|
4401
|
+
} catch (err) {
|
|
4402
|
+
console.warn(`Failed to create scan branch: ${err.message}`);
|
|
4403
|
+
return { prNumber, sha, branched: false };
|
|
4404
|
+
}
|
|
4405
|
+
try {
|
|
4406
|
+
const newPrBody = `Security scan of closed PR #${prNumber} (\`${pr.head.ref}\`).
|
|
4407
|
+
|
|
4408
|
+
This PR contains no code changes \u2014 it exists so Synkro can post review findings on an active PR.`;
|
|
4409
|
+
const result = ghJson([
|
|
4410
|
+
"api",
|
|
4411
|
+
"-X",
|
|
4412
|
+
"POST",
|
|
4413
|
+
`/repos/${repo}/pulls`,
|
|
4414
|
+
"-f",
|
|
4415
|
+
`title=Synkro Scan: ${pr.title}`,
|
|
4416
|
+
"-f",
|
|
4417
|
+
`body=${newPrBody}`,
|
|
4418
|
+
"-f",
|
|
4419
|
+
`head=${scanBranch}`,
|
|
4420
|
+
"-f",
|
|
4421
|
+
`base=${pr.base.ref}`
|
|
4422
|
+
]);
|
|
4423
|
+
console.log(`Opened PR #${result.number} for scan findings.
|
|
4424
|
+
`);
|
|
4425
|
+
return { prNumber: result.number, sha: result.head.sha, branched: true };
|
|
4426
|
+
} catch (err) {
|
|
4427
|
+
console.warn(`Failed to open scan PR: ${err.message}`);
|
|
4428
|
+
return { prNumber, sha, branched: false };
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4377
4431
|
function getPrFiles(repo, prNumber) {
|
|
4378
4432
|
const data = ghJson([
|
|
4379
4433
|
"api",
|
|
@@ -4766,6 +4820,9 @@ async function scanPrCommand() {
|
|
|
4766
4820
|
}
|
|
4767
4821
|
console.log(`Synkro scan-pr: ${repo}#${prNumber} @ ${sha.slice(0, 7)}
|
|
4768
4822
|
`);
|
|
4823
|
+
const prTarget = await ensureOpenPr(repo, prNumber, sha);
|
|
4824
|
+
const activePrNumber = prTarget.prNumber;
|
|
4825
|
+
const activeSha = prTarget.sha;
|
|
4769
4826
|
const orgRules = await fetchOrgRules(gatewayUrl, synkroApiKey);
|
|
4770
4827
|
const auditRules = orgRules.filter((r) => r.mode === "audit");
|
|
4771
4828
|
const literalNegativeRules = orgRules.filter((r) => {
|
|
@@ -4776,22 +4833,22 @@ async function scanPrCommand() {
|
|
|
4776
4833
|
const promptHeader = buildPrPrompt(auditRules);
|
|
4777
4834
|
let files;
|
|
4778
4835
|
try {
|
|
4779
|
-
files = getPrFiles(repo,
|
|
4836
|
+
files = getPrFiles(repo, activePrNumber);
|
|
4780
4837
|
} catch (err) {
|
|
4781
4838
|
console.error("Failed to fetch PR files:", err.message);
|
|
4782
4839
|
process.exit(2);
|
|
4783
4840
|
}
|
|
4784
|
-
const scanCtx = await fetchScanContext(gatewayUrl, synkroApiKey, repo,
|
|
4841
|
+
const scanCtx = await fetchScanContext(gatewayUrl, synkroApiKey, repo, activePrNumber, activeSha);
|
|
4785
4842
|
if (scanCtx.skip) {
|
|
4786
|
-
console.log(`Already scanned at ${
|
|
4843
|
+
console.log(`Already scanned at ${activeSha.slice(0, 7)}, skipping.
|
|
4787
4844
|
`);
|
|
4788
|
-
postCheckRun(repo,
|
|
4845
|
+
postCheckRun(repo, activeSha, "success", []);
|
|
4789
4846
|
await postEventToBackend({
|
|
4790
4847
|
gatewayUrl,
|
|
4791
4848
|
apiKey: synkroApiKey,
|
|
4792
4849
|
repo,
|
|
4793
|
-
prNumber,
|
|
4794
|
-
sha,
|
|
4850
|
+
prNumber: activePrNumber,
|
|
4851
|
+
sha: activeSha,
|
|
4795
4852
|
findings: [],
|
|
4796
4853
|
filesScanned: 0,
|
|
4797
4854
|
totalLatencyMs: 0
|
|
@@ -4814,13 +4871,13 @@ async function scanPrCommand() {
|
|
|
4814
4871
|
console.log(`${files.length} files in PR, ${eligible.length} eligible for scan.
|
|
4815
4872
|
`);
|
|
4816
4873
|
if (eligible.length === 0) {
|
|
4817
|
-
postCheckRun(repo,
|
|
4874
|
+
postCheckRun(repo, activeSha, "success", []);
|
|
4818
4875
|
await postEventToBackend({
|
|
4819
4876
|
gatewayUrl,
|
|
4820
4877
|
apiKey: synkroApiKey,
|
|
4821
4878
|
repo,
|
|
4822
|
-
prNumber,
|
|
4823
|
-
sha,
|
|
4879
|
+
prNumber: activePrNumber,
|
|
4880
|
+
sha: activeSha,
|
|
4824
4881
|
findings: [],
|
|
4825
4882
|
filesScanned: 0,
|
|
4826
4883
|
totalLatencyMs: 0
|
|
@@ -4847,17 +4904,17 @@ Total: ${allFindings.length} finding(s) across ${eligible.length} file(s) in ${t
|
|
|
4847
4904
|
const review = await spawnOpusConsolidator(allFindings, claudeToken);
|
|
4848
4905
|
console.log(` \u2192 ${review.comments.length} review comment(s), severity: ${review.severity}`);
|
|
4849
4906
|
if (review.comments.length > 0) {
|
|
4850
|
-
postPrReview(repo,
|
|
4907
|
+
postPrReview(repo, activePrNumber, activeSha, review);
|
|
4851
4908
|
}
|
|
4852
4909
|
}
|
|
4853
4910
|
const conclusion = shouldFail(allFindings, failThreshold) ? "failure" : "success";
|
|
4854
|
-
postCheckRun(repo,
|
|
4911
|
+
postCheckRun(repo, activeSha, conclusion, allFindings);
|
|
4855
4912
|
await postEventToBackend({
|
|
4856
4913
|
gatewayUrl,
|
|
4857
4914
|
apiKey: synkroApiKey,
|
|
4858
4915
|
repo,
|
|
4859
|
-
prNumber,
|
|
4860
|
-
sha,
|
|
4916
|
+
prNumber: activePrNumber,
|
|
4917
|
+
sha: activeSha,
|
|
4861
4918
|
findings: allFindings,
|
|
4862
4919
|
filesScanned: eligible.length,
|
|
4863
4920
|
totalLatencyMs
|