FOSSology  4.6.0
Open Source License Compliance by Open Source Software
cyclonedx.php
Go to the documentation of this file.
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2023 Sushant Kumar(sushantmishra02102002@gmail.com)
4 
5  SPDX-License-Identifier: GPL-2.0-only
6 */
20 namespace Fossology\CycloneDX;
21 
33 
34 include_once(__DIR__ . "/version.php");
35 include_once(__DIR__ . "/reportgenerator.php");
36 
41 class CycloneDXAgent extends Agent
42 {
43  const OUTPUT_FORMAT_KEY = "outputFormat";
44  const DEFAULT_OUTPUT_FORMAT = "cyclonedx_json";
45  const UPLOADS_ADD_KEY = "uploadsAdd";
46 
50  private $additionalUploads = [];
51 
60  private $reportutils;
64  private $uploadDao;
68  private $clearingDao;
73  private $licenseDao;
77  protected $dbManager;
81  private $licenseMap;
85  protected $agentNames = AgentRef::AGENT_LIST;
89  protected $uri;
94  private $licensesInDocument = [];
102  private $packageName;
103 
104  function __construct()
105  {
106  // deduce the agent name from the command line arguments
107  $args = getopt("", array(
108  self::OUTPUT_FORMAT_KEY.'::',
109  self::UPLOADS_ADD_KEY.'::'
110  ));
111  $agentName = "";
112  if (array_key_exists(self::OUTPUT_FORMAT_KEY, $args)) {
113  $agentName = trim($args[self::OUTPUT_FORMAT_KEY]);
114  }
115  if (empty($agentName)) {
117  }
118  if (array_key_exists(self::UPLOADS_ADD_KEY, $args)) {
119  $uploadsString = $args[self::UPLOADS_ADD_KEY];
120  if (!empty($uploadsString)) {
121  $this->additionalUploads = explode(',', $uploadsString);
122  }
123  }
124 
125  parent::__construct($agentName, AGENT_VERSION, AGENT_REV);
126 
127  $this->uploadDao = $this->container->get('dao.upload');
128  $this->clearingDao = $this->container->get('dao.clearing');
129  $this->licenseDao = $this->container->get('dao.license');
130  $this->dbManager = $this->container->get('db.manager');
131 
132  $this->reportutils = new ReportUtils();
133  $this->reportGenerator = new BomReportGenerator();
134  }
135 
140  function processUploadId($uploadId)
141  {
142  $this->licenseMap = new LicenseMap($this->dbManager, $this->groupId, LicenseMap::REPORT, true);
143 
144  $packageNodes = $this->renderPackage($uploadId);
145 
146  $this->computeUri($uploadId);
147 
148  $this->writeReport($packageNodes, $uploadId);
149  return true;
150  }
151 
157  protected function getUri($fileBase)
158  {
159  if (count($this->additionalUploads) > 0) {
160  $fileName = $fileBase . "multifile" . "_" . strtoupper($this->outputFormat);
161  } else {
162  $fileName = $fileBase. strtoupper($this->outputFormat)."_".$this->packageName;
163  }
164 
165  return $fileName .".json" ;
166  }
167 
172  protected function computeUri($uploadId)
173  {
174  global $SysConf;
175  $upload = $this->uploadDao->getUpload($uploadId);
176  $this->packageName = $upload->getFilename();
177 
178  $fileBase = $SysConf['FOSSOLOGY']['path']."/report/";
179 
180  $this->uri = $this->getUri($fileBase);
181  }
182 
188  protected function renderPackage($uploadId)
189  {
190  global $SysConf;
191  $uploadTreeTableName = $this->uploadDao->getUploadtreeTableName($uploadId);
192  $itemTreeBounds = $this->uploadDao->getParentItemBounds($uploadId, $uploadTreeTableName);
193  $this->heartbeat(0);
194 
195  $filesWithLicenses = $this->reportutils
196  ->getFilesWithLicensesFromClearings($itemTreeBounds, $this->groupId,
197  $this, $this->licensesInDocument);
198  $this->heartbeat(0);
199 
200  $this->reportutils->addClearingStatus($filesWithLicenses, $itemTreeBounds, $this->groupId);
201  $this->heartbeat(0);
202 
203  $this->reportutils->addScannerResults($filesWithLicenses, $itemTreeBounds, $this->groupId, $this->licensesInDocument);
204  $this->heartbeat(0);
205 
206  $this->reportutils->addCopyrightResults($filesWithLicenses, $uploadId);
207  $this->heartbeat(0);
208 
209  $upload = $this->uploadDao->getUpload($uploadId);
210  $components = $this->generateFileComponents($filesWithLicenses, $upload->getTreeTableName(), $uploadId, $itemTreeBounds);
211 
212  $mainLicenseIds = $this->clearingDao->getMainLicenseIds($uploadId, $this->groupId);
213  $mainLicenses = array();
214  foreach ($mainLicenseIds as $licId) {
215  $reportedLicenseId = $this->licenseMap->getProjectedId($licId);
216  $mainLicObj = $this->licenseDao->getLicenseById($reportedLicenseId, $this->groupId);
217  $licId = $mainLicObj->getId() . "-" . md5($mainLicObj->getText());
218  $licensedata['id'] = $mainLicObj->getSpdxId();
219  $licensedata['url'] = $mainLicObj->getUrl();
220  $mainLicenses[] = $this->reportGenerator->createLicense($licensedata);
221  }
222 
223  $hashes = $this->uploadDao->getUploadHashes($uploadId);
224  $serializedhash = array();
225  $serializedhash[] = $this->reportGenerator->createHash('SHA-1', $hashes['sha1']);
226  $serializedhash[] = $this->reportGenerator->createHash('MD5', $hashes['md5']);
227  // Check if sha256 is not empty
228  if (array_key_exists('sha256', $hashes) && !empty($hashes['sha256'])) {
229  $serializedhash[] = $this->reportGenerator->createHash('SHA-256', $hashes['sha256']);
230  }
231 
232  $maincomponentData = array (
233  'bomref' => strval($uploadId),
234  'type' => 'library',
235  'name' => $upload->getFilename(),
236  'hashes' => $serializedhash,
237  'scope' => 'required',
238  'mimeType' => $this->getMimeType($uploadId),
239  'licenses' => $mainLicenses
240  );
241  $maincomponent = $this->reportGenerator->createComponent($maincomponentData);
242 
243  $bomdata = array (
244  'tool-version' => $SysConf['BUILD']['VERSION'],
245  'maincomponent' => $maincomponent,
246  'components' => $components
247  );
248 
249  return $this->reportGenerator->generateReport($bomdata);
250  }
251 
259  protected function generateFileComponents($filesWithLicenses, $treeTableName, $uploadId, $itemTreeBounds)
260  {
261  /* @var $treeDao TreeDao */
262  $treeDao = $this->container->get('dao.tree');
263 
264  $filesProceeded = 0;
265  $lastValue = 0;
266  $components = array();
267  foreach ($filesWithLicenses as $fileId => $licenses) {
268  $filesProceeded += 1;
269  if (($filesProceeded & 2047) == 0) {
270  $this->heartbeat($filesProceeded - $lastValue);
271  $lastValue = $filesProceeded;
272  }
273 
274  $hashes = $treeDao->getItemHashes($fileId);
275  $serializedhash = array();
276  $serializedhash[] = $this->reportGenerator->createHash('SHA-1', $hashes['sha1']);
277  $serializedhash[] = $this->reportGenerator->createHash('MD5', $hashes['md5']);
278  // Check if sha256 is not empty
279  if (array_key_exists('sha256', $hashes) && !empty($hashes['sha256'])) {
280  $serializedhash[] = $this->reportGenerator->createHash('SHA-256', $hashes['sha256']);
281  }
282 
283  $fileName = $treeDao->getFullPath($fileId, $treeTableName, 0);
284  $licensesfound = [];
285 
286  if (!empty($licenses->getConcludedLicenses())) {
287  foreach ($licenses->getConcludedLicenses() as $licenseId) {
288  if (array_key_exists($licenseId, $this->licensesInDocument)) {
289  $licensedata = array(
290  "id" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getSpdxId(),
291  "name" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getFullName(),
292  "url" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getUrl()
293  );
294  $licensesfound[] = $this->reportGenerator->createLicense($licensedata);
295  }
296  }
297  } else {
298  foreach ($licenses->getScanners() as $licenseId) {
299  if (array_key_exists($licenseId, $this->licensesInDocument)) {
300  $licensedata = array(
301  "id" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getSpdxId(),
302  "name" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getFullName(),
303  "url" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getUrl()
304  );
305  $licensesfound[] = $this->reportGenerator->createLicense($licensedata);
306  }
307  }
308  }
309  if (!empty($fileName)) {
310  $mimeType = $this->getFileMimeType($fileId, $treeTableName);
311  $componentdata = array(
312  'bomref' => $uploadId .'-'. $fileId,
313  'type' => 'file',
314  'name' => $fileName,
315  'hashes' => $serializedhash,
316  'mimeType' => $mimeType,
317  'copyright' => implode("\n", $licenses->getCopyrights()),
318  'licenses' => $licensesfound
319  );
320  $components[] = $this->reportGenerator->createComponent($componentdata);
321  }
322  }
323  $this->heartbeat($filesProceeded - $lastValue);
324  return $components;
325  }
326 
332  protected function writeReport($packageNodes, $uploadId)
333  {
334  $fileBase = dirname($this->uri);
335 
336  if (!is_dir($fileBase)) {
337  mkdir($fileBase, 0777, true);
338  }
339  umask(0133);
340 
341  $contents = json_encode($packageNodes, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
342  // To ensure the file is valid, replace any non-printable characters with a question mark.
343  // 'Non-printable' is ASCII < 0x20 (excluding \r, \n and tab) and 0x7F - 0x9F.
344  $contents = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/u','?',$contents);
345  file_put_contents($this->uri, $contents);
346  $this->updateReportTable($uploadId, $this->jobId, $this->uri);
347  }
348 
355  protected function updateReportTable($uploadId, $jobId, $fileName)
356  {
357  $this->reportutils->updateOrInsertReportgenEntry($uploadId, $jobId, $fileName);
358  }
359 
364  protected function getMimeType($uploadId)
365  {
366  $sql = "SELECT mimetype_name
367  FROM upload u
368  JOIN pfile pf ON u.pfile_fk = pf.pfile_pk
369  JOIN mimetype m ON pf.pfile_mimetypefk = m.mimetype_pk
370  WHERE u.upload_pk = $1";
371 
372  $row = $this->dbManager->getSingleRow($sql, [$uploadId], __METHOD__);
373  return $row['mimetype_name'];
374  }
375 
382  protected function getFileMimeType($fileId, $treeTableName)
383  {
384  $sql = "SELECT m.mimetype_name
385  FROM $treeTableName ut
386  JOIN pfile pf ON ut.pfile_fk = pf.pfile_pk
387  LEFT JOIN mimetype m ON pf.pfile_mimetypefk = m.mimetype_pk
388  WHERE ut.uploadtree_pk = $1";
389 
390  $row = $this->dbManager->getSingleRow($sql, [$fileId], __METHOD__);
391  return $row['mimetype_name'] ?? 'application/octet-stream';
392  }
393 }
394 
395 $agent = new CycloneDXAgent();
396 $agent->scheduler_connect();
397 $agent->run_scheduler_event_loop();
398 $agent->scheduler_disconnect(0);
const OUTPUT_FORMAT_KEY
Argument key for output format.
Definition: cyclonedx.php:43
getFileMimeType($fileId, $treeTableName)
Get the mime type of a file.
Definition: cyclonedx.php:382
writeReport($packageNodes, $uploadId)
Write the report the file and update report table.
Definition: cyclonedx.php:332
updateReportTable($uploadId, $jobId, $fileName)
Update the reportgen table with new report path.
Definition: cyclonedx.php:355
renderPackage($uploadId)
Given an upload id, render the report string.
Definition: cyclonedx.php:188
getMimeType($uploadId)
Get the mime type of the upload.
Definition: cyclonedx.php:364
processUploadId($uploadId)
Given an upload ID, process the items in it.
Definition: cyclonedx.php:140
getUri($fileBase)
Get the URI for the given package.
Definition: cyclonedx.php:157
const DEFAULT_OUTPUT_FORMAT
Default output format.
Definition: cyclonedx.php:44
computeUri($uploadId)
For a given upload, compute the URI.
Definition: cyclonedx.php:172
generateFileComponents($filesWithLicenses, $treeTableName, $uploadId, $itemTreeBounds)
Generate the components by files.
Definition: cyclonedx.php:259
Structure of an Agent with all required parameters.
Definition: Agent.php:41
heartbeat($newProcessed)
Send hear beat to the scheduler.
Definition: Agent.php:203
Wrapper class for license map.
Definition: LicenseMap.php:19
char * trim(char *ptext)
Trimming whitespace.
Definition: fossconfig.c:690
int jobId
The id of the job.
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
FUNCTION char * strtoupper(char *s)
Helper function to upper case a string.
Definition: utils.c:39
Namespace used by CycloneDX agent.