Create a malware exporter
<!--IssueSummary start-->
<details>
<summary>
Everyone can contribute. [Help move this issue forward](https://handbook.gitlab.com/handbook/marketing/developer-relations/contributor-success/community-contributors-workflows/#contributor-links) while earning points, leveling up and collecting rewards.
</summary>
- [Work on this issue](https://contributors.gitlab.com/manage-issue?action=work&projectId=278964&issueIid=587162)
- [Close this issue](https://contributors.gitlab.com/manage-issue?action=close&projectId=278964&issueIid=587162)
</details>
<!--IssueSummary end-->
### Proposal
The refinement of this issue depends on https://gitlab.com/gitlab-org/gitlab/-/work_items/584273+
The main idea is that the exporter will have an execution mode for malware advisories for which it will read data from the `malware_db_advisory` table and export them in a v3 format in a private bucket.
The format of the data needs to be agreed. Also due to the v3 format the exporter will most probably be required to take additional steps including compression.
### Requirements
* ~~When updating manifest.json file in v3 in the bucket we need to make sure that the file is not being downloaded by instances.~~
* We need to keep a schema and version it with [SchemaVer](https://docs.snowplow.io/docs/api-reference/iglu/common-architecture/schemaver/). See https://gitlab.com/gitlab-org/gitlab/-/issues/587162#note_3064506944. Schema should be stored in https://gitlab.com/gitlab-org/security-products/security-report-schemas
* We need a module that translates OSV versions into NPM and other PURL type expressions. We can use this [algorithm](https://ossf.github.io/osv-schema/#evaluation). See https://gitlab.com/gitlab-org/gitlab/-/issues/587162#note_3115896155. Read OSV [spec docs](https://ossf.github.io/osv-schema/#affectedversions-field).
* Before exporting a JSON object make sure it is validated against the schema
### Suggested Schema
<details>
<summary>Schema</summary>
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "GLAM Malware Advisory",
"type": "object",
"required": ["malware_advisory", "package"],
"properties": {
"malware_advisory": {
"type": "object",
"required": ["id", "source", "title", "description", "published_date", "identifiers"],
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the malware advisory",
"pattern": "^GLAM-[0-9]{4}-[0-9]{2}-[0-9]{5,}$"
},
"source": {
"type": "string",
"description": "Source of the advisory",
"const": "glam"
},
"title": {
"type": "string",
"description": "Title of the malware advisory"
},
"description": {
"type": "string",
"description": "Detailed description of the malware"
},
"published_date": {
"type": "string",
"description": "Date when the advisory was published",
"format": "date"
},
"withdrawn": {
"type": "string",
"description": "Optional date when the advisory was withdrawn",
"format": "date"
},
"identifiers": {
"type": "array",
"description": "List of identifiers for this malware advisory",
"minItems": 1,
"items": {
"type": "object",
"required": ["type", "name", "value", "url"],
"properties": {
"type": {
"type": "string",
"description": "Type of identifier (e.g., GLAM, MAL)"
},
"name": {
"type": "string",
"description": "Name/display value of the identifier"
},
"value": {
"type": "string",
"description": "Actual identifier value"
},
"url": {
"type": "string",
"description": "URL reference for the identifier (may be empty)"
}
}
}
}
}
},
"package": {
"type": "object",
"description": "The affected package",
"required": ["name", "purl_type", "affected_range"],
"properties": {
"name": {
"type": "string",
"description": "Name of the affected package"
},
"purl_type": {
"type": "string",
"description": "Package URL type (e.g., npm, pypi, maven)"
},
"affected_range": {
"type": "string",
"description": "Version range affected by the malware"
}
}
}
}
}
```
</details>
### Proposal for breaking down work
- We need a module that parses the OSV advisory affected versions and gives a PURL specific type range ([semver_dialect](https://gitlab.com/gitlab-org/ruby/gems/semver_dialects)).
- We need to be able to always extract a full_data.zst file. Extend the glam-exporter with this capability. This needs to be a module that can be reused in the future by other exporter types.
- We need to add all the functionality that we have for public advisories but now also for malwares. Ignoring ful_data.zst.
- We need to write in the bucket the manifest.json file. We can start doing that for all data. Not only for malware.
- Create a scheduled job for exporter
- Update Deployment documentation, scripts and test E2E
epic