/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
adev/scripts/update-cross-repo-docs/github-client.mjs
88 строк
2 KB
Joey Perrott
fix(docs-infra): secure update-assets script against RCE and SSRF
02 июн 2026, 12:21
02 июн 2026, 12:21
3093edc
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import {get} from 'node:https'; import {posix} from 'node:path'; const GITHUB_API = 'https://api.github.com/repos/'; const SHA_REGEX = /^[0-9a-f]{40}$/i; const BRANCH_REGEX = /^(?!.*\.\.)[a-zA-Z0-9/_.-]+$/; export class GithubClient { #token; #ua; #api; constructor(repo, token, ua) { this.#token = token; this.#ua = ua; this.#api = posix.join(GITHUB_API, repo); } /** * Get the affected files. * * @param {string} baseSha * @param {string} headSha * @returns Promise<string[]> */ async getAffectedFiles(baseSha, headSha) { if (!SHA_REGEX.test(baseSha)) { throw new Error(`Invalid base SHA: ${baseSha}`); } if (!SHA_REGEX.test(headSha)) { throw new Error(`Invalid head SHA: ${headSha}`); } const {files} = JSON.parse(await this.#httpGet(`${this.#api}/compare/${baseSha}...${headSha}`)); return files.map((f) => f.filename); } /** * Get SHA of a branch. * * @param {string} branch * @returns Promise<string> */ async getShaForBranch(branch) { if (!BRANCH_REGEX.test(branch)) { throw new Error(`Invalid branch name: ${branch}`); } const sha = await this.#httpGet(`${this.#api}/commits/${branch}`, { headers: {Accept: 'application/vnd.github.VERSION.sha'}, }); if (!sha) { throw new Error(`Unable to extract the SHA for '${branch}'.`); } return sha.trim(); } #httpGet(url, options = {}) { options.headers ??= {}; options.headers['Authorization'] = `token ${this.#token}`; // User agent is required // https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#user-agent-required options.headers['User-Agent'] = this.#ua; return new Promise((resolve, reject) => { get(url, options, (res) => { let data = ''; res .on('data', (chunk) => { data += chunk; }) .on('end', () => { resolve(data); }); }).on('error', (e) => { reject(e); }); }); } }