FOSSology  4.5.1
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  if (!array_key_exists($licId, $this->licensesInDocument)) {
219  $this->licensesInDocument = (new SpdxLicenseInfo())
220  ->setLicenseObj($mainLicObj)
221  ->setCustomText(false)
222  ->setTextPrinted(true)
223  ->setListedLicense(true);
224  }
225  $licensedata['id'] = $mainLicObj->getSpdxId();
226  $licensedata['url'] = $mainLicObj->getUrl();
227  $mainLicenses[] = $this->reportGenerator->createLicense($licensedata);
228  }
229 
230  $hashes = $this->uploadDao->getUploadHashes($uploadId);
231  $serializedhash = array();
232  $serializedhash[] = $this->reportGenerator->createHash('SHA-1', $hashes['sha1']);
233  $serializedhash[] = $this->reportGenerator->createHash('MD5', $hashes['md5']);
234  // Check if sha256 is not empty
235  if (array_key_exists('sha256', $hashes) && !empty($hashes['sha256'])) {
236  $serializedhash[] = $this->reportGenerator->createHash('SHA-256', $hashes['sha256']);
237  }
238 
239  $maincomponentData = array (
240  'bomref' => strval($uploadId),
241  'type' => 'library',
242  'name' => $upload->getFilename(),
243  'hashes' => $serializedhash,
244  'scope' => 'required',
245  'mimeType' => $this->getMimeType($uploadId),
246  'licenses' => $mainLicenses
247  );
248  $maincomponent = $this->reportGenerator->createComponent($maincomponentData);
249 
250  $bomdata = array (
251  'tool-version' => $SysConf['BUILD']['VERSION'],
252  'maincomponent' => $maincomponent,
253  'components' => $components
254  );
255 
256  return $this->reportGenerator->generateReport($bomdata);
257  }
258 
266  protected function generateFileComponents($filesWithLicenses, $treeTableName, $uploadId, $itemTreeBounds)
267  {
268  /* @var $treeDao TreeDao */
269  $treeDao = $this->container->get('dao.tree');
270 
271  $filesProceeded = 0;
272  $lastValue = 0;
273  $components = array();
274  foreach ($filesWithLicenses as $fileId => $licenses) {
275  $filesProceeded += 1;
276  if (($filesProceeded & 2047) == 0) {
277  $this->heartbeat($filesProceeded - $lastValue);
278  $lastValue = $filesProceeded;
279  }
280 
281  $hashes = $treeDao->getItemHashes($fileId);
282  $serializedhash = array();
283  $serializedhash[] = $this->reportGenerator->createHash('SHA-1', $hashes['sha1']);
284  $serializedhash[] = $this->reportGenerator->createHash('MD5', $hashes['md5']);
285  // Check if sha256 is not empty
286  if (array_key_exists('sha256', $hashes) && !empty($hashes['sha256'])) {
287  $serializedhash[] = $this->reportGenerator->createHash('SHA-256', $hashes['sha256']);
288  }
289 
290  $fileName = $treeDao->getFullPath($fileId, $treeTableName, 0);
291  $licensesfound = [];
292 
293  if (!empty($licenses->getConcludedLicenses())) {
294  foreach ($licenses->getConcludedLicenses() as $licenseId) {
295  if (array_key_exists($licenseId, $this->licensesInDocument)) {
296  $licensedata = array(
297  "id" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getSpdxId(),
298  "name" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getFullName(),
299  "url" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getUrl()
300  );
301  $licensesfound[] = $this->reportGenerator->createLicense($licensedata);
302  }
303  }
304  } else {
305  foreach ($licenses->getScanners() as $licenseId) {
306  if (array_key_exists($licenseId, $this->licensesInDocument)) {
307  $licensedata = array(
308  "id" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getSpdxId(),
309  "name" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getFullName(),
310  "url" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getUrl()
311  );
312  $licensesfound[] = $this->reportGenerator->createLicense($licensedata);
313  }
314  }
315  }
316  if (!empty($fileName)) {
317  $componentdata = array(
318  'bomref' => $uploadId .'-'. $fileId,
319  'type' => 'file',
320  'name' => $fileName,
321  'hashes' => $serializedhash,
322  'mimeType' => 'text/plain',
323  'copyright' => implode("\n", $licenses->getCopyrights()),
324  'licenses' => $licensesfound
325  );
326  $components[] = $this->reportGenerator->createComponent($componentdata);
327  }
328  }
329  $this->heartbeat($filesProceeded - $lastValue);
330  return $components;
331  }
332 
338  protected function writeReport($packageNodes, $uploadId)
339  {
340  $fileBase = dirname($this->uri);
341 
342  if (!is_dir($fileBase)) {
343  mkdir($fileBase, 0777, true);
344  }
345  umask(0133);
346 
347  $contents = json_encode($packageNodes, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
348  // To ensure the file is valid, replace any non-printable characters with a question mark.
349  // 'Non-printable' is ASCII < 0x20 (excluding \r, \n and tab) and 0x7F - 0x9F.
350  $contents = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/u','?',$contents);
351  file_put_contents($this->uri, $contents);
352  $this->updateReportTable($uploadId, $this->jobId, $this->uri);
353  }
354 
361  protected function updateReportTable($uploadId, $jobId, $fileName)
362  {
363  $this->reportutils->updateOrInsertReportgenEntry($uploadId, $jobId, $fileName);
364  }
365 
370  protected function getMimeType($uploadId)
371  {
372  $sql = "SELECT mimetype_name
373  FROM upload u
374  JOIN pfile pf ON u.pfile_fk = pf.pfile_pk
375  JOIN mimetype m ON pf.pfile_mimetypefk = m.mimetype_pk
376  WHERE u.upload_pk = $1";
377 
378  $row = $this->dbManager->getSingleRow($sql, [$uploadId], __METHOD__);
379  return $row['mimetype_name'];
380  }
381 }
382 
383 $agent = new CycloneDXAgent();
384 $agent->scheduler_connect();
385 $agent->run_scheduler_event_loop();
386 $agent->scheduler_disconnect(0);
const OUTPUT_FORMAT_KEY
Argument key for output format.
Definition: cyclonedx.php:43
writeReport($packageNodes, $uploadId)
Write the report the file and update report table.
Definition: cyclonedx.php:338
updateReportTable($uploadId, $jobId, $fileName)
Update the reportgen table with new report path.
Definition: cyclonedx.php:361
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:370
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:266
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.