Skip to content

Commit eafb989

Browse files
authored
Merge pull request #11 from nexB/10-vul-package-json-schema
Add JSON schema for Package and Vulnerability metadata
2 parents a8b8653 + f1b8f28 commit eafb989

7 files changed

Lines changed: 456 additions & 1 deletion

File tree

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/nexB/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
import json
11+
12+
from django.core.management.base import BaseCommand
13+
from pydantic.json_schema import GenerateJsonSchema
14+
15+
from fedcode import schemas
16+
17+
18+
class GenerateFederatedCodeJsonSchema(GenerateJsonSchema):
19+
def generate(self, schema, mode="validation"):
20+
json_schema = super().generate(schema, mode=mode)
21+
json_schema["$schema"] = self.schema_dialect
22+
return json_schema
23+
24+
25+
def get_ordered_schema(schema, schema_path):
26+
schema["$id"] = f"https://raw.githubusercontent.com/nexB/federatedcode/main/{schema_path}"
27+
desired_order = [
28+
"$schema",
29+
"$id",
30+
"title",
31+
"type",
32+
]
33+
34+
ordered_schema = {key: schema[key] for key in desired_order}
35+
ordered_schema.update({key: schema[key] for key in schema if key not in desired_order})
36+
return ordered_schema
37+
38+
39+
def gen_schema(model_schema, path):
40+
schema = model_schema.model_json_schema(schema_generator=GenerateFederatedCodeJsonSchema)
41+
ordered_schema = get_ordered_schema(schema=schema, schema_path=path)
42+
with open(path, "w", encoding="utf-8") as f:
43+
json.dump(ordered_schema, f, indent=2)
44+
45+
46+
class Command(BaseCommand):
47+
def handle(self, *args, **options):
48+
gen_schema(
49+
model_schema=schemas.Vulnerability,
50+
path="schemas/vulnerability.schema.json",
51+
)
52+
gen_schema(
53+
model_schema=schemas.Package,
54+
path="schemas/package.schema.json",
55+
)

fedcode/schemas.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/nexB/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
from ninja import ModelSchema
11+
from typing import List
12+
13+
from fedcode import models
14+
15+
16+
class RemoteActor(ModelSchema):
17+
class Meta:
18+
model = models.RemoteActor
19+
fields = ["url", "username", "created_at", "updated_at"]
20+
21+
22+
class Repository(ModelSchema):
23+
"""
24+
A git repository used as a backing storage for Package and vulnerability data
25+
"""
26+
class Meta:
27+
model = models.Repository
28+
exclude = ["admin"]
29+
30+
31+
class Vulnerability(ModelSchema):
32+
repo: Repository
33+
34+
class Meta:
35+
model = models.Vulnerability
36+
fields = "__all__"
37+
38+
class Reputation(ModelSchema):
39+
"""
40+
https://www.w3.org/TR/activitystreams-vocabulary/#dfn-like
41+
https://www.w3.org/ns/activitystreams#Dislike
42+
"""
43+
class Meta:
44+
model = models.Reputation
45+
fields = ["object_id", "voter", "positive"]
46+
47+
48+
class Note(ModelSchema):
49+
"""
50+
A Note is a message send by a Person or Package.
51+
The content is either a plain text message or structured YAML.
52+
If the author is a Package actor then the content is always YAML
53+
If the author is a Person actor then the content is always plain text
54+
https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note
55+
"""
56+
reputation: List[Reputation]
57+
reply_to: "Note"
58+
59+
class Meta:
60+
model = models.Note
61+
fields = "__all__"
62+
63+
64+
Note.model_rebuild()
65+
66+
67+
class Package(ModelSchema):
68+
"""
69+
A software package identified by its package url ( PURL ) ignoring versions
70+
"""
71+
remote_actor: RemoteActor
72+
notes: List[Note]
73+
74+
class Meta:
75+
model = models.Package
76+
exclude = ["service"]

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ django-environ==0.11.2
1919
django-oauth-toolkit==2.3.0
2020
django-rest-framework==0.1.0
2121
djangorestframework==3.14.0
22+
django-ninja==1.2.1
2223
docutils==0.20.1
2324
et-xmlfile==1.1.0
2425
exceptiongroup==1.1.1
@@ -68,6 +69,7 @@ pytest==7.3.2
6869
pytest-django==4.5.2
6970
pytest-xdist==3.5.0
7071
python-dateutil==2.8.2
72+
pydantic==2.8.2
7173
pytz==2023.3
7274
PyYAML==6.0.1
7375
readme-renderer==42.0

schemas/package.schema.json

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://raw.githubusercontent.com/nexB/federatedcode/main/schemas/package.schema.json",
4+
"title": "Package",
5+
"type": "object",
6+
"$defs": {
7+
"Note": {
8+
"description": "A Note is a message send by a Person or Package.\nThe content is either a plain text message or structured YAML.\nIf the author is a Package actor then the content is always YAML\nIf the author is a Person actor then the content is always plain text\nhttps://www.w3.org/TR/activitystreams-vocabulary/#dfn-note",
9+
"properties": {
10+
"reputation": {
11+
"items": {
12+
"$ref": "#/$defs/Reputation"
13+
},
14+
"title": "Reputation",
15+
"type": "array"
16+
},
17+
"reply_to": {
18+
"$ref": "#/$defs/Note"
19+
},
20+
"id": {
21+
"anyOf": [
22+
{
23+
"format": "uuid",
24+
"type": "string"
25+
},
26+
{
27+
"type": "null"
28+
}
29+
],
30+
"description": "The object's unique global identifier",
31+
"title": "Id"
32+
},
33+
"acct": {
34+
"maxLength": 200,
35+
"title": "Acct",
36+
"type": "string"
37+
},
38+
"content": {
39+
"title": "Content",
40+
"type": "string"
41+
},
42+
"mediaType": {
43+
"default": "text/plain",
44+
"maxLength": 20,
45+
"title": "mediaType",
46+
"type": "string"
47+
},
48+
"created_at": {
49+
"description": "A field to track when notes are created",
50+
"format": "date-time",
51+
"title": "Created At",
52+
"type": "string"
53+
},
54+
"updated_at": {
55+
"description": "A field to track when notes are updated",
56+
"format": "date-time",
57+
"title": "Updated At",
58+
"type": "string"
59+
}
60+
},
61+
"required": [
62+
"reputation",
63+
"reply_to",
64+
"acct",
65+
"content",
66+
"created_at",
67+
"updated_at"
68+
],
69+
"title": "Note",
70+
"type": "object"
71+
},
72+
"RemoteActor": {
73+
"properties": {
74+
"url": {
75+
"anyOf": [
76+
{
77+
"type": "string"
78+
},
79+
{
80+
"type": "null"
81+
}
82+
],
83+
"default": null,
84+
"title": "Url"
85+
},
86+
"username": {
87+
"maxLength": 100,
88+
"title": "Username",
89+
"type": "string"
90+
},
91+
"created_at": {
92+
"description": "A field to track when remote actor are created",
93+
"format": "date-time",
94+
"title": "Created At",
95+
"type": "string"
96+
},
97+
"updated_at": {
98+
"description": "A field to track when remote actor are updated",
99+
"format": "date-time",
100+
"title": "Updated At",
101+
"type": "string"
102+
}
103+
},
104+
"required": [
105+
"username",
106+
"created_at",
107+
"updated_at"
108+
],
109+
"title": "RemoteActor",
110+
"type": "object"
111+
},
112+
"Reputation": {
113+
"description": "https://www.w3.org/TR/activitystreams-vocabulary/#dfn-like\nhttps://www.w3.org/ns/activitystreams#Dislike",
114+
"properties": {
115+
"object_id": {
116+
"anyOf": [
117+
{
118+
"format": "uuid",
119+
"type": "string"
120+
},
121+
{
122+
"type": "null"
123+
}
124+
],
125+
"default": null,
126+
"title": "Object Id"
127+
},
128+
"voter": {
129+
"description": "security@vcio",
130+
"maxLength": 100,
131+
"title": "Voter",
132+
"type": "string"
133+
},
134+
"positive": {
135+
"default": true,
136+
"title": "Positive",
137+
"type": "boolean"
138+
}
139+
},
140+
"required": [
141+
"voter"
142+
],
143+
"title": "Reputation",
144+
"type": "object"
145+
}
146+
},
147+
"description": "A software package identified by its package url ( PURL ) ignoring versions",
148+
"properties": {
149+
"remote_actor": {
150+
"$ref": "#/$defs/RemoteActor"
151+
},
152+
"notes": {
153+
"items": {
154+
"$ref": "#/$defs/Note"
155+
},
156+
"title": "Notes",
157+
"type": "array"
158+
},
159+
"summary": {
160+
"description": "profile summary",
161+
"maxLength": 100,
162+
"title": "Summary",
163+
"type": "string"
164+
},
165+
"public_key": {
166+
"title": "Public Key",
167+
"type": "string"
168+
},
169+
"local": {
170+
"default": true,
171+
"title": "Local",
172+
"type": "boolean"
173+
},
174+
"id": {
175+
"anyOf": [
176+
{
177+
"format": "uuid",
178+
"type": "string"
179+
},
180+
{
181+
"type": "null"
182+
}
183+
],
184+
"description": "The object's unique global identifier",
185+
"title": "Id"
186+
},
187+
"purl": {
188+
"description": "PURL (no version) ex: @pkg:maven/org.apache.logging",
189+
"maxLength": 300,
190+
"title": "Purl",
191+
"type": "string"
192+
}
193+
},
194+
"required": [
195+
"remote_actor",
196+
"notes",
197+
"summary",
198+
"public_key",
199+
"purl"
200+
]
201+
}

0 commit comments

Comments
 (0)