Skip to content

Commit 6c1b2db

Browse files
committed
add Markdown serializer
Signed-off-by: Hervé Boutemy <[email protected]>
1 parent 5de8d19 commit 6c1b2db

File tree

4 files changed

+241
-1
lines changed

4 files changed

+241
-1
lines changed

src/cyclonedx/CliUtils.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ public static ConvertFormat AutoDetectConvertBomFormat(string filename)
5757
{
5858
return ConvertFormat.csv;
5959
}
60+
if (fileExtension == ".md")
61+
{
62+
return ConvertFormat.markdown;
63+
}
6064
else if (filename.ToLowerInvariant().EndsWith(".spdx.json", StringComparison.InvariantCulture))
6165
{
6266
return ConvertFormat.spdxjson;
@@ -116,7 +120,7 @@ public static async Task<Bom> InputBomHelper(string filename, ConvertFormat form
116120
}
117121
}
118122

119-
123+
120124
if (format == ConvertFormat.csv)
121125
{
122126
using var inputStream = filename == null ? Console.OpenStandardInput() : File.OpenRead(filename);
@@ -211,6 +215,11 @@ public static async Task<int> OutputBomHelper(Bom bom, ConvertFormat format, Spe
211215
var bomBytes = Encoding.UTF8.GetBytes(bomString);
212216
stream.Write(bomBytes);
213217
break;
218+
case ConvertFormat.markdown:
219+
var mdString = MarkdownSerializer.Serialize(bom);
220+
var mdBytes = Encoding.UTF8.GetBytes(mdString);
221+
stream.Write(mdBytes);
222+
break;
214223
case ConvertFormat.spdxjson:
215224
var spdxDoc = bom.ToSpdx();
216225
await CycloneDX.Spdx.Serialization.JsonSerializer.SerializeAsync(spdxDoc, stream);

src/cyclonedx/Commands/ConvertFormat.cs

+1
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ public enum ConvertFormat
2525
protobuf,
2626
csv,
2727
spdxjson,
28+
markdown,
2829
}
2930
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// This file is part of CycloneDX CLI Tool
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the “License”);
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an “AS IS” BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// SPDX-License-Identifier: Apache-2.0
16+
// Copyright (c) OWASP Foundation. All Rights Reserved.
17+
using System;
18+
using System.Collections.Generic;
19+
using System.Diagnostics.Contracts;
20+
using System.IO;
21+
using System.Text;
22+
using CycloneDX.Models;
23+
24+
namespace CycloneDX.Cli.Serialization
25+
{
26+
public static class MarkdownSerializer
27+
{
28+
public static string Serialize(Bom bom)
29+
{
30+
Contract.Requires(bom != null);
31+
using (var stream = new MemoryStream())
32+
using (var writer = new StreamWriter(stream))
33+
{
34+
if (bom.Metadata?.Component != null)
35+
{
36+
writer.WriteLine("# " + bom.Metadata.Component.Name + " (" + bom.Metadata.Component.Type + ")");
37+
}
38+
writer.WriteLine("BOM " + bom.SerialNumber + " " + bom.BomFormat + " " + bom.SpecVersionString + " " + bom.Version + " " + bom.Metadata?.Timestamp);
39+
writer.WriteLine();
40+
41+
if (bom.Metadata != null)
42+
{
43+
var m = bom.Metadata;
44+
45+
// BOM tools or authors
46+
if (m.Tools != null)
47+
{
48+
writer.Write("BOM done with tools: ");
49+
foreach (var t in m.Tools)
50+
{
51+
writer.Write(t.Name + " " + t.Version + " (" + t.Vendor + "), ");
52+
}
53+
writer.WriteLine();
54+
}
55+
else if (m.Authors != null)
56+
{
57+
writer.Write("BOM authored by: ");
58+
foreach (var a in m.Authors)
59+
{
60+
writer.Write(a.Name + ", ");
61+
}
62+
writer.WriteLine();
63+
}
64+
65+
// Component
66+
if (m.Component != null)
67+
{
68+
var c = m.Component;
69+
70+
writer.Write(">");
71+
if (c.Group != null)
72+
{
73+
writer.Write(" _group:_ **" + c.Group + "**");
74+
}
75+
if (c.Name != null)
76+
{
77+
writer.Write(" _name:_ **" + c.Name + "**");
78+
}
79+
if (c.Version != null)
80+
{
81+
writer.Write(" _version:_ **" + c.Version + "**");
82+
}
83+
if (c.Name == null && c.Purl != null)
84+
{
85+
writer.Write(" _purl:_ **" + c.Purl + "**");
86+
}
87+
if (c.Cpe != null)
88+
{
89+
writer.Write(" _CPE:_ **" + c.Cpe + "**");
90+
}
91+
writer.WriteLine();
92+
if (c.Description != null)
93+
{
94+
writer.WriteLine(">");
95+
writer.WriteLine("> " + c.Description);
96+
}
97+
writer.WriteLine();
98+
99+
if (c.ExternalReferences != null)
100+
{
101+
writer.Write("Component external references: ");
102+
foreach (var er in c.ExternalReferences)
103+
{
104+
writer.Write("[" + er.Type + "](" + er.Url + "), ");
105+
}
106+
writer.WriteLine();
107+
}
108+
}
109+
110+
if (bom.ExternalReferences != null)
111+
{
112+
writer.WriteLine("## ExternalReferences");
113+
writer.WriteLine("not supported yet"); // how is it different from bom.Metadata.Component.ExternalReferences?
114+
writer.WriteLine();
115+
}
116+
117+
if (bom.Components != null)
118+
{
119+
writer.WriteLine("## Components");
120+
foreach (var c in bom.Components)
121+
{
122+
writer.Write("1. " + c.Type);
123+
if (c.Group != null)
124+
{
125+
writer.Write(" _group:_ **" + c.Group + "**");
126+
}
127+
if (c.Name != null)
128+
{
129+
writer.Write(" _name:_ **" + c.Name + "**");
130+
}
131+
if (c.Version != null)
132+
{
133+
writer.Write(" _version:_ **" + c.Version + "**");
134+
}
135+
if (c.Scope != null)
136+
{
137+
writer.Write(" _scope:_ " + c.Scope);
138+
}
139+
if (c.Purl != null)
140+
{
141+
writer.WriteLine();
142+
writer.Write(" _purl:_ " + c.Purl);
143+
}
144+
writer.WriteLine();
145+
}
146+
writer.WriteLine();
147+
}
148+
149+
if (bom.Services != null)
150+
{
151+
writer.WriteLine("## Services");
152+
writer.WriteLine("not supported yet");
153+
writer.WriteLine();
154+
}
155+
156+
if (bom.Dependencies != null) {
157+
writer.WriteLine("## Dependencies");
158+
WriteDependencies(writer, bom.Dependencies, "");
159+
writer.WriteLine();
160+
}
161+
162+
if (bom.Compositions != null)
163+
{
164+
writer.WriteLine("## Compositions");
165+
writer.WriteLine("not supported yet");
166+
writer.WriteLine();
167+
}
168+
169+
if (bom.Vulnerabilities != null)
170+
{
171+
writer.WriteLine("## Vulnerabilities");
172+
writer.WriteLine("not supported yet");
173+
writer.WriteLine();
174+
}
175+
}
176+
writer.Flush();
177+
return Encoding.UTF8.GetString(stream.ToArray());
178+
}
179+
}
180+
181+
private static void WriteDependencies(StreamWriter writer, List<Dependency> dependencies, string indent)
182+
{
183+
foreach (var d in dependencies)
184+
{
185+
writer.WriteLine(indent + "- " + d.Ref);
186+
if (d.Dependencies != null) {
187+
WriteDependencies(writer, d.Dependencies, indent + " ");
188+
}
189+
}
190+
}
191+
192+
public static Bom Deserialize(string csv)
193+
{
194+
return null;
195+
}
196+
}
197+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# commons-compress (Library)
2+
BOM urn:uuid:381cbf4c-3009-4a42-a4d0-7188e72fbfac CycloneDX 1.4 1 25/10/2022 19:46:32
3+
4+
BOM done with tools: CycloneDX Maven plugin 2.7.1 (OWASP Foundation),
5+
> _group:_ **org.apache.commons** _name:_ **commons-compress** _version:_ **1.22**
6+
>
7+
> Apache Commons Compress software defines an API for working with compression and archive formats. These include: bzip2, gzip, pack200, lzma, xz, Snappy, traditional Unix Compress, DEFLATE, DEFLATE64, LZ4, Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
8+
9+
Component external references: [Website](https://www.apache.org/), [BuildSystem](https://github.com/apache/commons-parent/actions), [Distribution](https://repository.apache.org/service/local/staging/deploy/maven2), [IssueTracker](https://issues.apache.org/jira/browse/COMPRESS), [MailingList](https://mail-archives.apache.org/mod_mbox/commons-user/), [Vcs](https://gitbox.apache.org/repos/asf?p=commons-compress.git),
10+
## Components
11+
1. Library _group:_ **com.github.luben** _name:_ **zstd-jni** _version:_ **1.5.2-5** _scope:_ Required
12+
_purl:_ pkg:maven/com.github.luben/[email protected]?type=jar
13+
1. Library _group:_ **org.brotli** _name:_ **dec** _version:_ **0.1.2** _scope:_ Required
14+
_purl:_ pkg:maven/org.brotli/[email protected]?type=jar
15+
1. Library _group:_ **org.tukaani** _name:_ **xz** _version:_ **1.9** _scope:_ Required
16+
_purl:_ pkg:maven/org.tukaani/[email protected]?type=jar
17+
1. Library _group:_ **org.ow2.asm** _name:_ **asm** _version:_ **9.4** _scope:_ Required
18+
_purl:_ pkg:maven/org.ow2.asm/[email protected]?type=jar
19+
1. Library _group:_ **org.osgi** _name:_ **org.osgi.core** _version:_ **6.0.0** _scope:_ Optional
20+
_purl:_ pkg:maven/org.osgi/[email protected]?type=jar
21+
22+
## Dependencies
23+
- pkg:maven/org.apache.commons/[email protected]?type=jar
24+
- pkg:maven/com.github.luben/[email protected]?type=jar
25+
- pkg:maven/org.brotli/[email protected]?type=jar
26+
- pkg:maven/org.tukaani/[email protected]?type=jar
27+
- pkg:maven/org.ow2.asm/[email protected]?type=jar
28+
- pkg:maven/org.osgi/[email protected]?type=jar
29+
- pkg:maven/com.github.luben/[email protected]?type=jar
30+
- pkg:maven/org.brotli/[email protected]?type=jar
31+
- pkg:maven/org.tukaani/[email protected]?type=jar
32+
- pkg:maven/org.ow2.asm/[email protected]?type=jar
33+
- pkg:maven/org.osgi/[email protected]?type=jar

0 commit comments

Comments
 (0)