Skip to content

Commit

Permalink
feat(scorecard): link to visualizer (#337)
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreDemailly authored Mar 16, 2024
1 parent 0308512 commit e7a7927
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 17 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"@nodesecure/flags": "^2.4.0",
"@nodesecure/ossf-scorecard-sdk": "^3.2.1",
"@nodesecure/rc": "^1.5.0",
"@nodesecure/scanner": "^5.2.1",
"@nodesecure/scanner": "^5.3.0",
"@nodesecure/utils": "^1.2.0",
"@openally/mutex": "^1.0.0",
"@topcli/spinner": "^2.1.2",
Expand Down
24 changes: 23 additions & 1 deletion public/scripts/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const kChartOptions = {
}
};
const kDefaultAvatarName = "../public/img/avatar-default.png";

const kScorecardVisualizerUrl = `https://kooltheba.github.io/openssf-scorecard-api-visualizer/#/projects`;
const colorRangeInfo = {
colorStart: 0.2, colorEnd: 0.8, useEndAsStart: false
};
Expand Down Expand Up @@ -108,6 +108,22 @@ function nodeDepNavigateLink(e) {
}
}


function liPackageNavigateScorecardLink(e) {
const dataRepo = this.getAttribute("data-repo");
const dataPlatform = this.getAttribute("data-platform");

if (!dataRepo) {
return;
}
else if (e.type === "click" || e.key === "Enter") {
const ref = e.target ?? e.srcElement;
if (ref) {
window.open(`${kScorecardVisualizerUrl}/${dataPlatform}/${dataRepo}`, "_blank");
}
}
}

document.addEventListener("DOMContentLoaded", () => {
const avatarsElements = document.querySelectorAll(".avatar");
for (const avatar of avatarsElements) {
Expand All @@ -134,6 +150,12 @@ document.addEventListener("DOMContentLoaded", () => {
liElement.addEventListener("keydown", nodeDepNavigateLink);
}

const scorecardPackagesList = document.querySelectorAll("ul.scorecard-packages-list li");
for (const liElement of scorecardPackagesList) {
liElement.addEventListener("click", liPackageNavigateScorecardLink);
liElement.addEventListener("keydown", liPackageNavigateScorecardLink);
}

setTimeout(() => {
window.isReadyForPDF = true;
}, 1000);
Expand Down
45 changes: 39 additions & 6 deletions src/analysis/extractScannerData.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,33 @@ import { getScoreColor } from "../utils.js";
// CONSTANTS
const kWantedFlags = Flags.getFlags();

function splitPackageWithOrg(pkg) {
// reverse here so if there is no orgPrefix, its value will be undefined
const [name, orgPrefix] = pkg.split("/").reverse();

return { orgPrefix, name };
}

export function getVCSRepositoryPathAndPlatform(url) {
if (!url) {
return null;
}

try {
const repo = new URL(url);

const repoPath = repo.pathname.slice(
1,
repo.pathname.includes(".git") ? -4 : repo.pathname.length
);

return [repoPath, repo.host];
}
catch {
return null;
}
}

/**
*
* @param {string[] | NodeSecure.Payload | NodeSecure.Payload[]} payloadFiles
Expand Down Expand Up @@ -74,21 +101,20 @@ export async function buildStatsFromNsecurePayloads(payloadFiles = [], options =
}

if (!(name in stats.packages)) {
const isGiven = config.npm?.packages.includes(name);
const org = config.npm?.organizationPrefix;
const fullName = isGiven && org ? `${org}/${name}` : name;
const { orgPrefix, name: splitName } = splitPackageWithOrg(name);
const isGiven = config.npm?.packages.includes(splitName) && orgPrefix === config.npm?.organizationPrefix;
if (isThird) {
stats.packages_count.external++;
}
stats.packages[name] = { isThird, versions: new Set(), fullName, isGiven };
stats.packages[name] = { isThird, versions: new Set(), fullName: name, isGiven };
}

const curr = stats.packages[name];
for (const [localVersion, localDescriptor] of Object.entries(versions)) {
if (curr.versions.has(localVersion)) {
continue;
}
const { flags, size, composition, license, author, warnings = [] } = localDescriptor;
const { flags, size, composition, license, author, warnings = [], links } = localDescriptor;

stats.size.all += size;
stats.size[isThird ? "external" : "internal"] += size;
Expand Down Expand Up @@ -135,6 +161,10 @@ export async function buildStatsFromNsecurePayloads(payloadFiles = [], options =
stats.deps.transitive.add(`${name}@${localVersion}`);
}
curr[localVersion] = { hasIndirectDependencies };

if (!curr.links) {
Object.assign(curr, { links });
}
}
}
}
Expand All @@ -144,9 +174,12 @@ export async function buildStatsFromNsecurePayloads(payloadFiles = [], options =
await Promise.all(givenPackages.map(async(pkg) => {
const { fullName } = pkg;
const { score } = await scorecard.result(fullName, { resolveOnVersionControl: false });
const [repo, platform] = getVCSRepositoryPathAndPlatform(pkg.links?.repository) ?? [];
stats.scorecards[fullName] = {
score,
color: getScoreColor(score)
color: getScoreColor(score),
repo,
platform
};
}));

Expand Down
21 changes: 18 additions & 3 deletions src/analysis/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,34 @@ import * as scanner from "./scanner.js";
import * as localStorage from "../localStorage.js";
import * as utils from "../utils.js";

function formatNpmPackages(organizationPrefix, packages) {
if (organizationPrefix === "") {
return packages;
}

return packages.map((pkg) => {
// in case the user has already added the organization prefix
if (pkg.startsWith(organizationPrefix)) {
return pkg;
}

return `${organizationPrefix}/${pkg}`;
});
}

export async function fetchPackagesAndRepositoriesData() {
const config = localStorage.getConfig().report;

const fetchNpm = "npm" in config && config.npm.packages.length > 0;
const fetchGit = "git" in config && config.git.repositories.length > 0;
const fetchNpm = config.npm?.packages.length > 0;
const fetchGit = config.git?.repositories.length > 0;
if (!fetchGit && !fetchNpm) {
throw new Error(
"No git repositories and no npm packages to fetch in the local configuration!"
);
}

const pkgStats = fetchNpm ?
await fetchPackagesStats(config.npm.packages) : null;
await fetchPackagesStats(formatNpmPackages(config.npm.organizationPrefix, config.npm.packages)) : null;

const { repositories, organizationUrl } = config.git;
const repoStats = fetchGit ?
Expand Down
12 changes: 6 additions & 6 deletions views/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ <h3>Authors & Maintainers</h3>
<div class="box-title">
<h3>Scorecards</h3>
</div>
<ul class="npm-packages-list">
[[ for (const [package, { score, color }] of Object.entries(z.npm_stats.scorecards)) { ]]
<li class="scorecard-item">
<ul class="scorecard-packages-list">
[[ for (const [package, { score, color, repo, platform }] of Object.entries(z.npm_stats.scorecards)) { ]]
<li class="scorecard-item" data-repo="[[=repo]]" data-platform="[[=platform]]">
<span class="package">[[=package]]</span>
<span class="score [[=color]]">[[=score]]</span>
</li>
Expand Down Expand Up @@ -261,9 +261,9 @@ <h3>Authors & Maintainers</h3>
<div class="box-title">
<h3>Scorecards</h3>
</div>
<ul class="npm-packages-list">
[[ for (const [package, { score, color }] of Object.entries(z.git_stats.scorecards)) { ]]
<li class="scorecard-item">
<ul class="scorecard-packages-list">
[[ for (const [package, { score, color, repo, platform }] of Object.entries(z.git_stats.scorecards)) { ]]
<li class="scorecard-item" data-repo="[[=repo]]" data-platform="[[=platform]]">
<span class="package">[[=package]]</span>
<span class="score [[=color]]">[[=score]]</span>
</li>
Expand Down

0 comments on commit e7a7927

Please sign in to comment.