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 17, 2021
1 parent 486a92e commit ddf6a01
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 18 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/export-osv.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Export to OSV format

on:
push:
branches:
- export-osv

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

steps:
- uses: actions/checkout@v2

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

- run: |
git config user.name github-actions
git config user.email github-actions@github.com
composer install --prefer-dist --no-progress
php export-osv.php packagist
git add .
git stash
git fetch origin osv
git checkout osv
rm -rf packagist
git stash pop
git commit -m "Update OSV data export" || 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());

foreach ($fileSystemIterator as $fileInfo) {
echo 'Converting "' . $namespace . '/' . $package . '" ...' . str_repeat(' ', 20) . "\r";
$osv = convertToOsv($fileInfo, $namespace . '/' . $package);
// TODO Handle duplicate IDs
$path = $targetFolder . DIRECTORY_SEPARATOR . $osv['id'] . '.json';

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

echo PHP_EOL;

0 comments on commit ddf6a01

Please sign in to comment.