Skip to content

Commit 4d5046f

Browse files
committed
Add JSON schema for Package and Vulnerability metadata
Signed-off-by: Keshav Priyadarshi <[email protected]>
1 parent a8b8653 commit 4d5046f

File tree

6 files changed

+390
-0
lines changed

6 files changed

+390
-0
lines changed
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: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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 djantic import ModelSchema
11+
from pydantic import ConfigDict
12+
from typing import List
13+
14+
from fedcode import models
15+
16+
17+
class RemoteActor(ModelSchema):
18+
model_config = ConfigDict(model=models.RemoteActor, exclude=["service", "package", "person"])
19+
20+
21+
class Repository(ModelSchema):
22+
model_config = ConfigDict(
23+
model=models.Repository,
24+
exclude=["id", "vulnerability_set", "review_set", "syncrequest_set", "admin"],
25+
)
26+
27+
28+
class Vulnerability(ModelSchema):
29+
repo: Repository
30+
model_config = ConfigDict(model=models.Vulnerability, exclude=["id"])
31+
32+
33+
class Reputation(ModelSchema):
34+
model_config = ConfigDict(model=models.Reputation, exclude=["object_id" , "id"])
35+
36+
37+
class Note(ModelSchema):
38+
reputation: List[Reputation]
39+
reply_to: "Note"
40+
model_config = ConfigDict(
41+
model=models.Note, exclude=["id", "replies", "package_set", "person_set", "review_set"]
42+
)
43+
44+
45+
Note.model_rebuild()
46+
47+
48+
class Package(ModelSchema):
49+
remote_actor: RemoteActor
50+
notes: List[Note]
51+
model_config = ConfigDict(model=models.Package, exclude=["id", "follow_set", "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+
djantic2==1.0.3
2223
docutils==0.20.1
2324
et-xmlfile==1.1.0
2425
exceptiongroup==1.1.1
@@ -63,6 +64,7 @@ psycopg-binary==3.1.16
6364
psycopg2-binary==2.9.9
6465
pycodestyle==2.11.1
6566
pycparser==2.21
67+
pydantic==2.8.2
6668
Pygments==2.17.2
6769
pytest==7.3.2
6870
pytest-django==4.5.2

schemas/package.schema.json

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

0 commit comments

Comments
 (0)