Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added SDK #447

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions sdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
venv
__pycache__
.idea
.DS-store
89 changes: 89 additions & 0 deletions sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# OpenCRE SDK

The OpenCRE SDK is a Python (3.11 and later) library that provides a convenient interface for interacting with the OpenCRE API. It includes classes for handling Common Requirements Enumeration (CRE) data, links, and associated documents.


## Installation

```bash
pip install opencre-sdk
```

## Usage


### Create an OpenCRE instance

```python
from opencre import OpenCRE

# Initialize OpenCRE SDK
opencre = OpenCRE()
```

### Configuration
The `OpenCREConfig` class allows you to configure the OpenCRE SDK with your specific settings. By default, it is configured with the OpenCRE base URL and API prefix.

```python
from opencre_sdk import OpenCREConfig, OpenCRE

# Create an OpenCRE configuration instance
config = OpenCREConfig()

# Optionally, customize the configuration
config.HOST_URL = "https://example.org/"
config.API_PREFIX = "custom/v1/"

# Create an OpenCRE instance with the custom configuration
opencre = OpenCRE(config=config)
```

## Interacting with CREs

The `OpenCRE` class provides methods for interacting with CREs:

### Retrieving Root CREs

To retrieve a list of root CREs:

```python
root_cres = opencre.root_cres()
print(root_cres)
```

### Retrieve a specific CRE by ID

```python
cre_id = "170-772"
cre = opencre.cre(cre_id)
print(str(cre)) # Outputs: 'CRE 170-772'
print(cre.name) # Outputs: 'Cryptography'
print(cre.id) # Outputs: '170-772'
```

## Handling Links and Documents

The `Link` class represents a link associated with a CRE, and the `Document` class represents a document associated with a CRE. Additional document types (`Standard`, `Tool`, and `CRELink`) extend the `Document` class.

### Access links of a CRE

```python
cre = opencre.cre("170-772")
links = cre.links
link = links[5]
print(link.ltype) # Outputs: 'Linked To'
doc = link.document
print(doc.name) # Outputs: 'Cloud Controls Matrix'
```

#### Link Types (ltype)

`ltype` attribute in the `Link` class represents the type of relationship between the CRE and the linked document. Currently, there are two possible values for ltype:

- **`Contains`**: This indicates that the CRE ncludes the content of the linked document. For instance, a CRE about "Manual penetration testing" might contain another CRE about "Dynamic security testing".

- **`Linked To`**: This signifies a reference to an external standard, tool, or another CRE. For example, a CRE might be linked to a specific section in the "NIST SSDF" standard.

## Contributing

We welcome contributions! Please submit pull requests for bug fixes, features, and improvements. See [**Contributing**](https://github.com/OWASP/OpenCRE/blob/main/CONTRIBUTING.md) for contributing instructions.
1 change: 1 addition & 0 deletions sdk/opencre/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .opencre import OpenCRE
196 changes: 196 additions & 0 deletions sdk/opencre/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
from __future__ import annotations
from requests import Response


class Link:
"""
Represents a link associated with a CRE.

Attributes:
- raw (dict): The raw data of the link.
- ltype (str): The type of the link.

Methods:
- parse_from_cre(cls, cre: CRE) -> list[Link]: Parses links from a CRE object.
- get_document_class(self): Determines the document class associated with the link's doctype.
- document(self): Retrieves the associated document instance.
"""
def __init__(self, raw, ltype):
self.raw = raw
self.ltype = ltype

@classmethod
def parse_from_cre(cls, cre: CRE) -> list[Link]:
"""
Parses links from a CRE object.

Parameters:
- cre (CRE): The CRE object.

Returns:
list[Link]: A list of Link objects parsed from the CRE.
"""
links_raw = cre.raw.get("links")
links = [cls(raw=link_raw, ltype=link_raw["ltype"]) for link_raw in links_raw]
return links

def get_document_class(self):
"""
Determines the document class associated with the link's doctype.

Returns:
document_class: The class of the associated document.
"""
document_parent = Document.parse_from_link(link=self)
doctype = document_parent.doctype
document_class = None

if doctype == "Standard":
document_class = Standard

if doctype == "CRE":
document_class = CRELink

if doctype == "Tool":
document_class = Tool

if document_class is None:
raise NotImplementedError("Not implemented for this doctype")

return document_class

@property
def document(self):
"""
Retrieves the associated document instance.

Returns:
Document: The associated document instance.
"""
document_class = self.get_document_class()
document = document_class.parse_from_link(self)
return document


class CRE:
"""
Represents a CRE (Common Requirements Enumeration).

Attributes:
- raw (dict): The raw data of the CRE.
- id (str): The identifier of the CRE.
- name (str): The name of the CRE.
- doctype (str): The type of the CRE.

Methods:
- links(self): Retrieves links associated with the CRE.
- parse_from_response(cls, response: Response, many: bool = False) -> CRE | list[CRE]: Parses CRE(s) from a response object.
- __str__(self): Returns a string representation of the CRE.
"""
def __init__(self, raw, cre_id, name, doctype):
self.raw = raw
self.id = cre_id
self.name = name
self.doctype = doctype

@property
def links(self):
"""
Retrieves links associated with the CRE.

Returns:
list[Link]: A list of Link objects associated with the CRE.
"""
return Link.parse_from_cre(cre=self)

@classmethod
def parse_from_response(cls, response: Response, many: bool = False) -> CRE | list[CRE]:
"""
Parses CRE(s) from a response object.

Parameters:
- response (Response): The response object.
- many (bool): True if expecting multiple CREs in the response, False otherwise.

Returns:
CRE | list[CRE]: A single CRE or a list of CREs parsed from the response.
"""
cres = []
data = response.json().get("data")

if not many:
data = [data]

for raw_cre in data:
cre = cls(
raw=raw_cre,
cre_id=raw_cre["id"],
name=raw_cre["name"],
doctype=raw_cre["doctype"]
)
cres.append(cre)

if not many:
return cres[0]

return cres

def __str__(self):
return f'CRE {self.id}'


class Document:
"""
Represents a document associated with a CRE.

Attributes:
- raw (dict): The raw data of the document.
- doctype (str): The type of the document.
- name (str): The name of the document.

Methods:
- parse_from_link(cls, link: Link) -> Document: Parses a document from a Link object.
"""
def __init__(self, raw, doctype, name):
self.raw = raw
self.doctype = doctype
self.name = name

@classmethod
def parse_from_link(cls, link: Link) -> Document:
"""
Parses a document from a Link object.

Parameters:
- link (Link): The Link object.

Returns:
Document: The Document object associated with the Link.
"""
document_raw = link.raw.get("document")
document = cls(
raw=document_raw,
doctype=document_raw["doctype"],
name=document_raw["name"]
)
return document


class Standard(Document):
...


class Tool(Document):
...


class CRELink(Document):
"""
Represents a link to another CRE document associated with a CRE.

Attributes:
- id: The identifier of the linked CRE.
"""
@property
def id(self):
return self.raw.get("id")
Loading
Loading