Skip to content

Commit

Permalink
Export advisories in OSV format
Browse files Browse the repository at this point in the history
  • Loading branch information
jaylinski committed Nov 18, 2021
1 parent 486a92e commit da8061d
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 18 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/export-osv.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Export to OSV format

on:
push:
branches:
- export-osv

jobs:
publish-web:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
with:
# Required in order to extract dates from commit history
fetch-depth: 0

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.0"
coverage: none
tools: composer

- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Export to OSV format
run: |
git config user.name github-actions
git config user.email [email protected]
php export-osv.php packagist
git add packagist
git stash
git checkout osv
echo `date` > published
git add published
git rm -r --ignore-unmatch packagist
git commit -m "Update OSV data export" || true
git stash pop
git commit --amend --no-edit --allow-empty || true
git push || true
36 changes: 18 additions & 18 deletions .github/workflows/php.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
name: Validation

on:
push:
pull_request:
push:
pull_request:

jobs:
run:
runs-on: ubuntu-latest
run:
runs-on: ubuntu-latest

name: Validation
steps:
- name: Checkout
uses: actions/checkout@v2
name: Validation
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.0"
coverage: none
tools: composer
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.0"
coverage: none
tools: composer

- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: Install dependencies
run: composer install --prefer-dist --no-progress

- name: Run tests
run: php -d memory_limit=-1 validator.php
- name: Run tests
run: php -d memory_limit=-1 validator.php
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ not** serve as the primary source of information for security issues, it is
not authoritative for any referenced software, but it allows to centralize
information for convenience and easy consumption.

We also export advisory data to the [OSV](https://github.com/ossf/osv-schema) format,
see the [`osv`](https://github.com/FriendsOfPHP/security-advisories/tree/osv) branch.

License
-------

Expand Down
119 changes: 119 additions & 0 deletions export-osv.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php

/**
* Script for exporting advisories to OSV format.
*
* Usage: `php export-osv.php target_folder`
*
* @see https://ossf.github.io/osv-schema/
*/

namespace FriendsOfPhp\SecurityAdvisories;

use DirectoryIterator;
use FilesystemIterator;
use SplFileInfo;
use Symfony\Component\Yaml\Yaml;

if (!is_file($autoloader = __DIR__ . '/vendor/autoload.php')) {
echo 'Dependencies are not installed, please run "composer install" first!' . PHP_EOL;
exit(1);
}

require $autoloader;

function convertToOsv(SplFileInfo $fileInfo, string $package): array
{
$advisory = Yaml::parseFile($fileInfo->getPathname());

return [
'id' => $advisory['cve'] ?? 'PHPSEC-' . $fileInfo->getBasename('.yaml'),
'modified' => getDateFromGitLog($fileInfo),
'published' => getDateFromGitLog($fileInfo, true),
'aliases' => [],
'related' => [],
'summary' => $advisory['title'] ?? '',
'details' => '',
'affected' => [
'package' => [
'ecosystem' => 'Packagist',
'name' => $package,
'purl' => sprintf('pkg:packagist/%s', $package),
],
'ranges' => [
'type' => 'SEMVER',
'events' => getEvents($advisory['branches']),
],
],
'references' => [
array_key_exists('link', $advisory) ? [
'type' => 'ADVISORY',
'url' => $advisory['link'],
] : null,
[
'type' => 'PACKAGE',
'url' => 'https://packagist.org/packages/' . $package,
],
],
];
}

function getEvents(array $branches): array
{
$events = [];

foreach (array_column($branches, 'versions') as $branch) {
if (count($branch) === 2) {
array_push($events, ['introduced' => $branch[0]]);
array_push($events, ['fixed' => $branch[1]]);
} else {
array_push($events, ['fixed' => $branch[0]]);
}
}

return $events;
}

function getDateFromGitLog(SplFileInfo $fileInfo, bool $created = false): string
{
$timestamp = shell_exec(sprintf(
'git log --format="%%at" %s %s %s %s',
$created ? '' : '--max-count 1',
$created ? '--reverse' : '',
escapeshellarg($fileInfo->getPathname()),
$created ? '| head -1' : ''
));

return date('Y-m-d\TH:i:s\Z', (int) trim($timestamp));
}

mkdir($targetFolder = $argv[1] ?? 'packagist');

$namespaceIterator = new DirectoryIterator(__DIR__);

// Package namespaces
foreach ($namespaceIterator as $namespaceInfo) {
if ($namespaceInfo->isDot() || !$namespaceInfo->isDir() || $namespaceInfo->getFilename() === 'vendor' || strpos($namespaceInfo->getFilename() , '.') === 0) continue;

$namespace = $namespaceInfo->getFilename();
$packageIterator = new DirectoryIterator($namespaceInfo->getPathname());

// Packages inside namespace
foreach ($packageIterator as $packageInfo) {
if ($packageIterator->isDot() || !$packageInfo->isDir()) continue;

$package = $packageInfo->getFilename();
$fileSystemIterator = new FilesystemIterator($packageInfo->getPathname());

echo 'Converting "' . $namespace . '/' . $package . '" ...' . str_repeat(' ', 20) . "\r";

foreach ($fileSystemIterator as $fileInfo) {
$osv = convertToOsv($fileInfo, $namespace . '/' . $package);
$path = $targetFolder . DIRECTORY_SEPARATOR . $osv['id'] . '.json';

file_put_contents($path, json_encode($osv, JSON_PRETTY_PRINT));
}
}
}

echo PHP_EOL;

0 comments on commit da8061d

Please sign in to comment.