Skip to content

Commit

Permalink
feat: resolveOnVersionControl option (#80)
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreDemailly authored Nov 26, 2023
1 parent 9f47547 commit 8ded855
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 21 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ jobs:

steps:
- name: Harden Runner
uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 # v2.5.1
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs

- name: Checkout repository
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@04daf014b50eaf774287bf3f0f1869d4b4c4b913 # v2.21.7
uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -63,7 +63,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@04daf014b50eaf774287bf3f0f1869d4b4c4b913 # v2.21.7
uses: github/codeql-action/autobuild@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
Expand All @@ -76,6 +76,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@04daf014b50eaf774287bf3f0f1869d4b4c4b913 # v2.21.7
uses: github/codeql-action/analyze@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
with:
category: "/language:${{matrix.language}}"
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ jobs:
fail-fast: false
steps:
- name: Harden Runner
uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 # v2.5.1
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs

- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ jobs:

steps:
- name: Harden Runner
uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 # v2.5.1
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs

- name: "Checkout code"
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false

- name: "Run analysis"
uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
with:
results_file: results.sarif
results_format: sarif
Expand Down Expand Up @@ -72,6 +72,6 @@ jobs:

# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@04daf014b50eaf774287bf3f0f1869d4b4c4b913 # v2.21.7
uses: github/codeql-action/upload-sarif@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8
with:
sarif_file: results.sarif
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ console.log(data);

You can disable `resolveOnNpmRegistry` option which is `true` by default.

> [!TIP]
> You can disable version control resolve when you are sure the given repository is well formatted using `resolveOnVersionControl: false`.
> This can save you from GitHub/GitLab rate limit when working with lots of repositories.
```ts
const data = await scorecard.result("NodeSecure/scanner", {
resolveOnNpmRegistry: false, // default to true
Expand All @@ -81,13 +85,22 @@ export interface IResultOptions {
* @default true
*/
resolveOnNpmRegistry?: boolean;
/**
* @description Try to resolve the given repository on the given platform. This can be useful when the given repository
* is not exactly the same as the one on the given platform (case sensitive).
* @default true
*/
resolveOnVersionControl?: boolean;
}
```

## API

### result(repository: string, options?: IResultOptions): Promise< ScorecardResult >

> [!TIP]
> You can use `GITHUB_TOKEN` environment variable to avoid rate-limit when `resolveOnVersionControl` is true.
Return the OpenSSF ScorecardResult for a given organization and repository.

The response is typed using the following set of types:
Expand Down
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,18 @@
"@nodesecure/eslint-config": "^1.8.0",
"@npm/types": "^1.0.2",
"@slimio/is": "^2.0.0",
"@types/node": "^20.6.1",
"@types/node": "^20.10.0",
"c8": "^8.0.1",
"eslint": "^8.49.0",
"glob": "^10.3.4",
"eslint": "^8.54.0",
"glob": "^10.3.10",
"is-svg": "^5.0.0",
"registry-url": "^6.0.1",
"tsx": "^3.12.10",
"typescript": "^5.2.2"
"tsx": "^4.5.0",
"typescript": "^5.3.2"
},
"dependencies": {
"@myunisoft/httpie": "^2.0.3",
"@nodesecure/npm-registry-sdk": "^1.6.1"
"@nodesecure/npm-registry-sdk": "^1.6.1",
"dotenv": "^16.3.1"
}
}
30 changes: 27 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { config } from "dotenv";
config();

// Import Third-party Dependencies
import { get } from "@myunisoft/httpie";
import { packument } from "@nodesecure/npm-registry-sdk";
Expand All @@ -9,6 +12,9 @@ import { GitHubRepository, GitLabProject } from "./utils/internalTypes.js";
// CONSTANTS
const kDefaultPlatform = "github.com";
const kGitHubApiUrl = "https://api.github.com";
const kGitHubRequestOptions = {
authorization: process.env.GITHUB_TOKEN ?? ""
};
const kGitLabApiUrl = "https://gitlab.com";
export const API_URL = "https://api.securityscorecards.dev";

Expand Down Expand Up @@ -49,6 +55,12 @@ export interface IResultOptions {
* @default true
*/
resolveOnNpmRegistry?: boolean;
/**
* @description Try to resolve the given repository on the given platform. This can be useful when the given repository
* is not exactly the same as the one on the given platform (case sensitive).
* @default true
*/
resolveOnVersionControl?: boolean;
}

async function getNpmRepository(repository: string): Promise<string> {
Expand All @@ -67,7 +79,8 @@ async function getNpmRepository(repository: string): Promise<string> {

async function retrieveRepositoryOnGithub(owner: string, repo: string): Promise<string> {
const { data } = await get<GitHubRepository>(
new URL(`/repos/${owner}/${repo}`, kGitHubApiUrl)
new URL(`/repos/${owner}/${repo}`, kGitHubApiUrl),
kGitHubRequestOptions
);

return data.full_name;
Expand All @@ -92,12 +105,13 @@ export async function result(
let formattedRepository = repository;
const {
platform = kDefaultPlatform,
resolveOnNpmRegistry = true
resolveOnNpmRegistry = true,
resolveOnVersionControl = true
} = options;
const [owner, repo] = repository.replace("@", "").split("/");

// We don't try to resolve repository via GitHub or NPM API when platform is GHES
if (platform !== "github.corp.com") {
if (platform !== "github.corp.com" && resolveOnVersionControl) {
try {
// We try to retrieve the repository with the GitHub or GitLab API because
// Scorecard API is case sensitive, i.e if we pass "nodesecure/cli",
Expand Down Expand Up @@ -127,6 +141,16 @@ export async function result(
}
}
}
else if (resolveOnNpmRegistry && !resolveOnVersionControl) {
try {
formattedRepository = await getNpmRepository(repository);
}
catch (error) {
throw new Error(`Invalid repository, cannot find it on NPM registry`, {
cause: error
});
}
}

const { data } = await get<ScorecardResult>(
new URL(`/projects/${platform}/${formattedRepository}`, API_URL)
Expand Down
36 changes: 36 additions & 0 deletions test/result.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,31 @@ describe("#result() FT", () => {
);
});

it("should return the ScorecardResult for NodeSecure/scanner when not resolving on GitHub", async() => {
const result = await scorecard.result("NodeSecure/scanner", {
resolveOnVersionControl: false,
resolveOnNpmRegistry: false
});

assert.equal(is.plainObject(result), true);
assert.equal(result.repo.name, `github.com/${kDefaultRepository}`);
assert.deepStrictEqual(
Object.keys(result).sort(),
["date", "repo", "scorecard", "score", "checks"].sort()
);
});

it("should throw for nodesecure/scanner when not resolving on GitHub", async() => {
// throws because the repository is malformed (should be exactly NodeSecure/scanner)
await assert.rejects(async() => await scorecard.result("nodesecure/scanner", {
resolveOnVersionControl: false,
resolveOnNpmRegistry: false
}), {
name: "Error",
message: "Not Found"
});
});

it("should return the ScorecardResult for @nodesecure/scanner", async() => {
const result = await scorecard.result(`@${kDefaultRepository.toLowerCase()}`);

Expand All @@ -129,6 +154,17 @@ describe("#result() FT", () => {
);
});

it("should return the ScorecardResult for @nodesecure/scanner when not resolving version control", async() => {
const result = await scorecard.result(`@${kDefaultRepository.toLowerCase()}`, { resolveOnVersionControl: false });

assert.equal(is.plainObject(result), true);
assert.equal(result.repo.name, `github.com/${kDefaultRepository}`);
assert.deepStrictEqual(
Object.keys(result).sort(),
["date", "repo", "scorecard", "score", "checks"].sort()
);
});

it("should return the ScorecardResult for gitlab-org/gitlab-ui (GitLab)", async() => {
const result = await scorecard.result("gitlab-org/gitlab-ui", { platform: "gitlab.com", resolveOnNpmRegistry: false });

Expand Down

0 comments on commit 8ded855

Please sign in to comment.