FOSSology  4.4.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 
54  private $reportutils;
58  private $uploadDao;
62  private $clearingDao;
67  private $licenseDao;
71  protected $dbManager;
75  private $licenseMap;
79  protected $agentNames = AgentRef::AGENT_LIST;
83  protected $uri;
88  private $licensesInDocument = [];
96  private $packageName;
97 
98  function __construct()
99  {
100  // deduce the agent name from the command line arguments
101  $args = getopt("", array(self::OUTPUT_FORMAT_KEY.'::'));
102  $agentName = "";
103  if (array_key_exists(self::OUTPUT_FORMAT_KEY, $args)) {
104  $agentName = trim($args[self::OUTPUT_FORMAT_KEY]);
105  }
106  if (empty($agentName)) {
108  }
109 
110  parent::__construct($agentName, AGENT_VERSION, AGENT_REV);
111 
112  $this->uploadDao = $this->container->get('dao.upload');
113  $this->clearingDao = $this->container->get('dao.clearing');
114  $this->licenseDao = $this->container->get('dao.license');
115  $this->dbManager = $this->container->get('db.manager');
116 
117  $this->reportutils = new ReportUtils();
118  $this->reportGenerator = new BomReportGenerator();
119  }
120 
125  function processUploadId($uploadId)
126  {
127  $this->licenseMap = new LicenseMap($this->dbManager, $this->groupId, LicenseMap::REPORT, true);
128 
129  $packageNodes = $this->renderPackage($uploadId);
130 
131  $this->computeUri($uploadId);
132 
133  $this->writeReport($packageNodes, $uploadId);
134  return true;
135  }
136 
142  protected function getUri($fileBase)
143  {
144  $fileName = $fileBase. strtoupper($this->outputFormat)."_".$this->packageName.'_'.date("Y-m-d_H:i:s");
145  return $fileName .".json" ;
146  }
147 
152  protected function computeUri($uploadId)
153  {
154  global $SysConf;
155  $upload = $this->uploadDao->getUpload($uploadId);
156  $this->packageName = $upload->getFilename();
157 
158  $fileBase = $SysConf['FOSSOLOGY']['path']."/report/";
159 
160  $this->uri = $this->getUri($fileBase);
161  }
162 
168  protected function renderPackage($uploadId)
169  {
170  global $SysConf;
171  $uploadTreeTableName = $this->uploadDao->getUploadtreeTableName($uploadId);
172  $itemTreeBounds = $this->uploadDao->getParentItemBounds($uploadId, $uploadTreeTableName);
173  $this->heartbeat(0);
174 
175  $filesWithLicenses = $this->reportutils
176  ->getFilesWithLicensesFromClearings($itemTreeBounds, $this->groupId,
177  $this, $this->licensesInDocument);
178  $this->heartbeat(0);
179 
180  $this->reportutils->addClearingStatus($filesWithLicenses, $itemTreeBounds, $this->groupId);
181  $this->heartbeat(0);
182 
183  $this->reportutils->addScannerResults($filesWithLicenses, $itemTreeBounds, $this->groupId, $this->licensesInDocument);
184  $this->heartbeat(0);
185 
186  $this->reportutils->addCopyrightResults($filesWithLicenses, $uploadId);
187  $this->heartbeat(0);
188 
189  $upload = $this->uploadDao->getUpload($uploadId);
190  $components = $this->generateFileComponents($filesWithLicenses, $upload->getTreeTableName(), $uploadId, $itemTreeBounds);
191 
192  $mainLicenseIds = $this->clearingDao->getMainLicenseIds($uploadId, $this->groupId);
193  $mainLicenses = array();
194  foreach ($mainLicenseIds as $licId) {
195  $reportedLicenseId = $this->licenseMap->getProjectedId($licId);
196  $mainLicObj = $this->licenseDao->getLicenseById($reportedLicenseId);
197  $licId = $mainLicObj->getId() . "-" . md5($mainLicObj->getText());
198  if (!array_key_exists($licId, $this->licensesInDocument)) {
199  $this->licensesInDocument = (new SpdxLicenseInfo())
200  ->setLicenseObj($mainLicObj)
201  ->setCustomText(false)
202  ->setTextPrinted(true)
203  ->setListedLicense(true);
204  }
205  $licensedata['id'] = $mainLicObj->getSpdxId();
206  $licensedata['url'] = $mainLicObj->getUrl();
207  $mainLicenses[] = $this->reportGenerator->createLicense($licensedata);
208  }
209 
210  $hashes = $this->uploadDao->getUploadHashes($uploadId);
211  $serializedhash = array();
212  $serializedhash[] = $this->reportGenerator->createHash('SHA-1', $hashes['sha1']);
213  $serializedhash[] = $this->reportGenerator->createHash('MD5', $hashes['md5']);
214  // Check if sha256 is not empty
215  if (array_key_exists('sha256', $hashes) && !empty($hashes['sha256'])) {
216  $serializedhash[] = $this->reportGenerator->createHash('SHA-256', $hashes['sha256']);
217  }
218 
219  $maincomponentData = array (
220  'bomref' => strval($uploadId),
221  'type' => 'library',
222  'name' => $upload->getFilename(),
223  'hashes' => $serializedhash,
224  'scope' => 'required',
225  'mimeType' => $this->getMimeType($uploadId),
226  'licenses' => $mainLicenses
227  );
228  $maincomponent = $this->reportGenerator->createComponent($maincomponentData);
229 
230  $bomdata = array (
231  'tool-version' => $SysConf['BUILD']['VERSION'],
232  'maincomponent' => $maincomponent,
233  'components' => $components
234  );
235 
236  return $this->reportGenerator->generateReport($bomdata);
237  }
238 
246  protected function generateFileComponents($filesWithLicenses, $treeTableName, $uploadId, $itemTreeBounds)
247  {
248  /* @var $treeDao TreeDao */
249  $treeDao = $this->container->get('dao.tree');
250 
251  $filesProceeded = 0;
252  $lastValue = 0;
253  $components = array();
254  foreach ($filesWithLicenses as $fileId => $licenses) {
255  $filesProceeded += 1;
256  if (($filesProceeded & 2047) == 0) {
257  $this->heartbeat($filesProceeded - $lastValue);
258  $lastValue = $filesProceeded;
259  }
260 
261  $hashes = $treeDao->getItemHashes($fileId);
262  $serializedhash = array();
263  $serializedhash[] = $this->reportGenerator->createHash('SHA-1', $hashes['sha1']);
264  $serializedhash[] = $this->reportGenerator->createHash('MD5', $hashes['md5']);
265  // Check if sha256 is not empty
266  if (array_key_exists('sha256', $hashes) && !empty($hashes['sha256'])) {
267  $serializedhash[] = $this->reportGenerator->createHash('SHA-256', $hashes['sha256']);
268  }
269 
270  $fileName = $treeDao->getFullPath($fileId, $treeTableName, 0);
271  $licensesfound = [];
272 
273  if (!empty($licenses->getConcludedLicenses())) {
274  foreach ($licenses->getConcludedLicenses() as $licenseId) {
275  if (array_key_exists($licenseId, $this->licensesInDocument)) {
276  $licensedata = array(
277  "id" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getSpdxId(),
278  "name" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getFullName(),
279  "url" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getUrl()
280  );
281  $licensesfound[] = $this->reportGenerator->createLicense($licensedata);
282  }
283  }
284  } else {
285  foreach ($licenses->getScanners() as $licenseId) {
286  if (array_key_exists($licenseId, $this->licensesInDocument)) {
287  $licensedata = array(
288  "id" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getSpdxId(),
289  "name" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getFullName(),
290  "url" => $this->licensesInDocument[$licenseId]->getLicenseObj()->getUrl()
291  );
292  $licensesfound[] = $this->reportGenerator->createLicense($licensedata);
293  }
294  }
295  }
296  if (!empty($fileName)) {
297  $componentdata = array(
298  'bomref' => $uploadId .'-'. $fileId,
299  'type' => 'file',
300  'name' => $fileName,
301  'hashes' => $serializedhash,
302  'mimeType' => 'text/plain',
303  'copyright' => implode("\n", $licenses->getCopyrights()),
304  'licenses' => $licensesfound
305  );
306  $components[] = $this->reportGenerator->createComponent($componentdata);
307  }
308  }
309  $this->heartbeat($filesProceeded - $lastValue);
310  return $components;
311  }
312 
318  protected function writeReport($packageNodes, $uploadId)
319  {
320  $fileBase = dirname($this->uri);
321 
322  if (!is_dir($fileBase)) {
323  mkdir($fileBase, 0777, true);
324  }
325  umask(0133);
326 
327  $contents = json_encode($packageNodes, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
328  // To ensure the file is valid, replace any non-printable characters with a question mark.
329  // 'Non-printable' is ASCII < 0x20 (excluding \r, \n and tab) and 0x7F - 0x9F.
330  $contents = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/u','?',$contents);
331  file_put_contents($this->uri, $contents);
332  $this->updateReportTable($uploadId, $this->jobId, $this->uri);
333  }
334 
341  protected function updateReportTable($uploadId, $jobId, $fileName)
342  {
343  $this->dbManager->insertTableRow('reportgen',
344  ['upload_fk'=>$uploadId, 'job_fk'=>$jobId, 'filepath'=>$fileName],
345  __METHOD__);
346  }
347 
352  protected function getMimeType($uploadId)
353  {
354  $sql = "SELECT mimetype_name
355  FROM upload u
356  JOIN pfile pf ON u.pfile_fk = pf.pfile_pk
357  JOIN mimetype m ON pf.pfile_mimetypefk = m.mimetype_pk
358  WHERE u.upload_pk = $1";
359 
360  $row = $this->dbManager->getSingleRow($sql, [$uploadId], __METHOD__);
361  return $row['mimetype_name'];
362  }
363 }
364 
365 $agent = new CycloneDXAgent();
366 $agent->scheduler_connect();
367 $agent->run_scheduler_event_loop();
368 $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:318
updateReportTable($uploadId, $jobId, $fileName)
Update the reportgen table with new report path.
Definition: cyclonedx.php:341
renderPackage($uploadId)
Given an upload id, render the report string.
Definition: cyclonedx.php:168
getMimeType($uploadId)
Get the mime type of the upload.
Definition: cyclonedx.php:352
processUploadId($uploadId)
Given an upload ID, process the items in it.
Definition: cyclonedx.php:125
getUri($fileBase)
Get the URI for the given package.
Definition: cyclonedx.php:142
const DEFAULT_OUTPUT_FORMAT
Default output format.
Definition: cyclonedx.php:44
computeUri($uploadId)
For a given upload, compute the URI.
Definition: cyclonedx.php:152
generateFileComponents($filesWithLicenses, $treeTableName, $uploadId, $itemTreeBounds)
Generate the components by files.
Definition: cyclonedx.php:246
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.