aiden-runtime 3.19.4 → 3.19.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/config/devos.config.backup.json +224 -224
  2. package/config/devos.config.json +1 -1
  3. package/dist/api/server.js +7 -1
  4. package/dist/core/agentLoop.js +8 -3
  5. package/dist/core/skillLoader.js +2 -0
  6. package/dist/core/slashAsTool.js +2 -0
  7. package/dist/core/toolRegistry.js +16 -0
  8. package/dist/core/version.js +1 -1
  9. package/dist/providers/router.js +46 -6
  10. package/dist-bundle/cli.js +911 -557
  11. package/dist-bundle/index.js +758 -658
  12. package/package.json +268 -266
  13. package/scripts/postinstall.js +58 -1
  14. package/workspace-templates/HEARTBEAT.md +16 -0
  15. package/workspace-templates/SOUL.md +267 -0
  16. package/workspace-templates/STANDING_ORDERS.md +21 -0
  17. package/workspace-templates/permissions.yaml +180 -0
  18. package/workspace-templates/skills/architecture-diagram/SKILL.md +126 -0
  19. package/workspace-templates/skills/architecture-diagram/skill.json +25 -0
  20. package/workspace-templates/skills/arxiv/SKILL.md +124 -0
  21. package/workspace-templates/skills/arxiv/skill.json +26 -0
  22. package/workspace-templates/skills/ascii-art/SKILL.md +142 -0
  23. package/workspace-templates/skills/ascii-art/skill.json +26 -0
  24. package/workspace-templates/skills/blogwatcher/SKILL.md +147 -0
  25. package/workspace-templates/skills/blogwatcher/skill.json +26 -0
  26. package/workspace-templates/skills/censys/SKILL.md +104 -0
  27. package/workspace-templates/skills/censys/index.ts +133 -0
  28. package/workspace-templates/skills/censys/skill.json +25 -0
  29. package/workspace-templates/skills/clipboard-history/SKILL.md +101 -0
  30. package/workspace-templates/skills/clipboard-history/skill.json +23 -0
  31. package/workspace-templates/skills/crt-sh/SKILL.md +102 -0
  32. package/workspace-templates/skills/crt-sh/index.ts +59 -0
  33. package/workspace-templates/skills/crt-sh/skill.json +25 -0
  34. package/workspace-templates/skills/cveapi/SKILL.md +114 -0
  35. package/workspace-templates/skills/cveapi/index.ts +249 -0
  36. package/workspace-templates/skills/cveapi/skill.json +25 -0
  37. package/workspace-templates/skills/docker-management/SKILL.md +156 -0
  38. package/workspace-templates/skills/docker-management/skill.json +25 -0
  39. package/workspace-templates/skills/excalidraw/SKILL.md +148 -0
  40. package/workspace-templates/skills/excalidraw/skill.json +25 -0
  41. package/workspace-templates/skills/explainshell/SKILL.md +93 -0
  42. package/workspace-templates/skills/explainshell/index.ts +132 -0
  43. package/workspace-templates/skills/explainshell/skill.json +25 -0
  44. package/workspace-templates/skills/financial_research/SKILL.md +21 -0
  45. package/workspace-templates/skills/financial_research/skill.json +24 -0
  46. package/workspace-templates/skills/gif-search/SKILL.md +122 -0
  47. package/workspace-templates/skills/gif-search/skill.json +25 -0
  48. package/workspace-templates/skills/github-auth/SKILL.md +134 -0
  49. package/workspace-templates/skills/github-auth/skill.json +26 -0
  50. package/workspace-templates/skills/github-issues/SKILL.md +130 -0
  51. package/workspace-templates/skills/github-issues/skill.json +25 -0
  52. package/workspace-templates/skills/github-pr-workflow/SKILL.md +143 -0
  53. package/workspace-templates/skills/github-pr-workflow/skill.json +26 -0
  54. package/workspace-templates/skills/github-repo-management/SKILL.md +147 -0
  55. package/workspace-templates/skills/github-repo-management/skill.json +26 -0
  56. package/workspace-templates/skills/google-workspace/SKILL.md +110 -0
  57. package/workspace-templates/skills/google-workspace/skill.json +26 -0
  58. package/workspace-templates/skills/greynoise/SKILL.md +96 -0
  59. package/workspace-templates/skills/greynoise/index.ts +107 -0
  60. package/workspace-templates/skills/greynoise/skill.json +25 -0
  61. package/workspace-templates/skills/haveibeenpwned/SKILL.md +100 -0
  62. package/workspace-templates/skills/haveibeenpwned/index.ts +72 -0
  63. package/workspace-templates/skills/haveibeenpwned/skill.json +24 -0
  64. package/workspace-templates/skills/jupyter-live-kernel/SKILL.md +116 -0
  65. package/workspace-templates/skills/jupyter-live-kernel/skill.json +25 -0
  66. package/workspace-templates/skills/linear/SKILL.md +107 -0
  67. package/workspace-templates/skills/linear/skill.json +25 -0
  68. package/workspace-templates/skills/nano-pdf/SKILL.md +113 -0
  69. package/workspace-templates/skills/nano-pdf/skill.json +26 -0
  70. package/workspace-templates/skills/notion/SKILL.md +108 -0
  71. package/workspace-templates/skills/notion/skill.json +24 -0
  72. package/workspace-templates/skills/obsidian/SKILL.md +115 -0
  73. package/workspace-templates/skills/obsidian/skill.json +24 -0
  74. package/workspace-templates/skills/ocr-and-documents/SKILL.md +125 -0
  75. package/workspace-templates/skills/ocr-and-documents/skill.json +26 -0
  76. package/workspace-templates/skills/p5js/SKILL.md +163 -0
  77. package/workspace-templates/skills/p5js/skill.json +24 -0
  78. package/workspace-templates/skills/research-paper-writing/SKILL.md +158 -0
  79. package/workspace-templates/skills/research-paper-writing/skill.json +26 -0
  80. package/workspace-templates/skills/securityheaders/SKILL.md +99 -0
  81. package/workspace-templates/skills/securityheaders/index.ts +213 -0
  82. package/workspace-templates/skills/securityheaders/skill.json +26 -0
  83. package/workspace-templates/skills/shodan/SKILL.md +113 -0
  84. package/workspace-templates/skills/shodan/index.ts +94 -0
  85. package/workspace-templates/skills/shodan/skill.json +26 -0
  86. package/workspace-templates/skills/songsee/SKILL.md +152 -0
  87. package/workspace-templates/skills/songsee/skill.json +25 -0
  88. package/workspace-templates/skills/ssllabs/SKILL.md +107 -0
  89. package/workspace-templates/skills/ssllabs/index.ts +208 -0
  90. package/workspace-templates/skills/ssllabs/skill.json +27 -0
  91. package/workspace-templates/skills/stable-diffusion-image-generation/SKILL.md +136 -0
  92. package/workspace-templates/skills/stable-diffusion-image-generation/skill.json +24 -0
  93. package/workspace-templates/skills/systematic-debugging/SKILL.md +131 -0
  94. package/workspace-templates/skills/systematic-debugging/skill.json +25 -0
  95. package/workspace-templates/skills/test-driven-development/SKILL.md +164 -0
  96. package/workspace-templates/skills/test-driven-development/skill.json +25 -0
  97. package/workspace-templates/skills/urlscan/SKILL.md +118 -0
  98. package/workspace-templates/skills/urlscan/index.ts +94 -0
  99. package/workspace-templates/skills/urlscan/skill.json +24 -0
  100. package/workspace-templates/skills/virustotal/SKILL.md +120 -0
  101. package/workspace-templates/skills/virustotal/index.ts +124 -0
  102. package/workspace-templates/skills/virustotal/skill.json +26 -0
  103. package/workspace-templates/skills/web_research/SKILL.md +18 -0
  104. package/workspace-templates/skills/web_research/skill.json +20 -0
  105. package/workspace-templates/skills/xitter/SKILL.md +148 -0
  106. package/workspace-templates/skills/xitter/skill.json +26 -0
  107. package/workspace-templates/skills/youtube-content/SKILL.md +121 -0
  108. package/workspace-templates/skills/youtube-content/skill.json +25 -0
@@ -0,0 +1,164 @@
1
+ ---
2
+ name: test-driven-development
3
+ description: Write software using the RED-GREEN-REFACTOR cycle for reliable, well-tested code
4
+ category: developer
5
+ version: 1.0.0
6
+ origin: aiden
7
+ license: Apache-2.0
8
+ tags: tdd, testing, red-green-refactor, unit-tests, pytest, jest, vitest, quality, development
9
+ ---
10
+
11
+ # Test-Driven Development (TDD)
12
+
13
+ Use the RED-GREEN-REFACTOR cycle to write reliable, well-tested software. Write tests first, then write the minimal code to pass them, then clean up.
14
+
15
+ ## When to Use
16
+
17
+ - User wants to implement a new function or module correctly
18
+ - User wants to ensure edge cases are handled before they become bugs
19
+ - User is refactoring and wants a safety net
20
+ - User wants tests for business-critical logic
21
+ - User wants to document behavior through executable tests
22
+
23
+ ## How to Use
24
+
25
+ ### The TDD cycle
26
+
27
+ ```
28
+ RED → Write a failing test for the behavior you want
29
+ GREEN → Write the minimum code to make the test pass
30
+ REFACTOR → Clean up the code without breaking tests
31
+ Repeat for each small behavior increment
32
+ ```
33
+
34
+ ### Phase 1: RED — Write a failing test first
35
+
36
+ Write the test before the implementation. It must fail to prove the test works.
37
+
38
+ ```python
39
+ # test_calculator.py — write this BEFORE calculator.py
40
+ import pytest
41
+ from calculator import add
42
+
43
+ def test_add_two_positive_numbers():
44
+ assert add(2, 3) == 5
45
+
46
+ def test_add_negative_number():
47
+ assert add(-1, 5) == 4
48
+
49
+ def test_add_floats():
50
+ assert abs(add(0.1, 0.2) - 0.3) < 1e-9
51
+ ```
52
+
53
+ Run the tests — they should fail with `ModuleNotFoundError` or `ImportError`:
54
+
55
+ ```powershell
56
+ pytest test_calculator.py -v
57
+ # Expected: FAILED (module not found)
58
+ ```
59
+
60
+ ### Phase 2: GREEN — Minimal implementation to pass
61
+
62
+ Write only enough code to make the tests pass — nothing more.
63
+
64
+ ```python
65
+ # calculator.py
66
+ def add(a, b):
67
+ return a + b
68
+ ```
69
+
70
+ ```powershell
71
+ pytest test_calculator.py -v
72
+ # Expected: 3 PASSED
73
+ ```
74
+
75
+ ### Phase 3: REFACTOR — Improve code quality
76
+
77
+ Clean up implementation and tests while keeping all tests green.
78
+
79
+ ```python
80
+ # calculator.py — add type hints and docstring
81
+ def add(a: float, b: float) -> float:
82
+ """Return the sum of a and b."""
83
+ return a + b
84
+ ```
85
+
86
+ ```powershell
87
+ pytest test_calculator.py -v # must still pass after refactor
88
+ ```
89
+
90
+ ### Write tests with pytest (Python)
91
+
92
+ ```python
93
+ # Parametrize for multiple cases
94
+ @pytest.mark.parametrize("a,b,expected", [
95
+ (2, 3, 5),
96
+ (-1, 5, 4),
97
+ (0, 0, 0),
98
+ (1.5, 2.5, 4.0),
99
+ ])
100
+ def test_add(a, b, expected):
101
+ assert add(a, b) == expected
102
+
103
+ # Test exceptions
104
+ def test_add_rejects_string():
105
+ with pytest.raises(TypeError):
106
+ add("hello", 2)
107
+ ```
108
+
109
+ ### Write tests with Vitest / Jest (TypeScript / JavaScript)
110
+
111
+ ```typescript
112
+ // calculator.test.ts
113
+ import { describe, it, expect } from 'vitest'
114
+ import { add } from './calculator'
115
+
116
+ describe('add()', () => {
117
+ it('adds two positive numbers', () => {
118
+ expect(add(2, 3)).toBe(5)
119
+ })
120
+
121
+ it('handles negative numbers', () => {
122
+ expect(add(-1, 5)).toBe(4)
123
+ })
124
+
125
+ it('throws on non-number input', () => {
126
+ expect(() => add('a' as any, 2)).toThrow()
127
+ })
128
+ })
129
+ ```
130
+
131
+ ### TDD for API endpoints
132
+
133
+ ```python
134
+ # test_api.py — RED first
135
+ def test_create_user_returns_201(client):
136
+ resp = client.post("/users", json={"name": "Alice", "email": "alice@example.com"})
137
+ assert resp.status_code == 201
138
+ assert resp.json()["id"] is not None
139
+
140
+ def test_create_user_rejects_missing_email(client):
141
+ resp = client.post("/users", json={"name": "Alice"})
142
+ assert resp.status_code == 422
143
+
144
+ # Then implement the /users endpoint to make these pass
145
+ ```
146
+
147
+ ## Examples
148
+
149
+ **"Implement a function that validates Indian phone numbers"**
150
+ → RED: write tests for +91 10-digit numbers, reject 9-digit, reject letters. GREEN: implement regex. REFACTOR: add named groups, docstring.
151
+
152
+ **"Add a discount calculator to the checkout module"**
153
+ → RED: test 10% off, 0% off, 100% off, reject negative discount. GREEN: minimal implementation. REFACTOR: extract constants, add type hints.
154
+
155
+ **"I want to refactor this function but keep it working"**
156
+ → Write characterization tests first (tests that document current behavior), then refactor with test safety net.
157
+
158
+ ## Cautions
159
+
160
+ - Never skip the RED phase — a test that was never failing might not actually test anything
161
+ - Tests should be independent — each test must set up its own state and not depend on test order
162
+ - Aim for one assertion per test — tests with multiple assertions are harder to diagnose when they fail
163
+ - TDD is slower upfront but faster overall — set this expectation with the user
164
+ - Mock external services (APIs, databases) in unit tests — test real integrations separately
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "test-driven-development",
3
+ "version": "1.0.0",
4
+ "description": "Write software using the RED-GREEN-REFACTOR cycle for reliable, well-tested code",
5
+ "author": "aiden",
6
+ "license": "MIT",
7
+ "tools": [],
8
+ "trigger_phrases": [],
9
+ "compatible_agents": [
10
+ "aiden"
11
+ ],
12
+ "min_agent_version": "3.0.0",
13
+ "tags": [
14
+ "tdd",
15
+ "testing",
16
+ "red-green-refactor",
17
+ "unit-tests",
18
+ "pytest",
19
+ "jest",
20
+ "vitest",
21
+ "quality",
22
+ "development"
23
+ ],
24
+ "created": "2026-04-27T17:11:40.912Z"
25
+ }
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: urlscan
3
+ description: Submit URLs for automated malware and phishing analysis, then retrieve safety verdicts and screenshots via urlscan.io
4
+ category: security
5
+ version: 1.0.0
6
+ license: Apache-2.0
7
+ origin: aiden
8
+ tags: security, malware, phishing, url, scan, osint, urlscan, threat-intel
9
+ env_required:
10
+ - URLSCAN_API_KEY
11
+ ---
12
+
13
+ # URL Safety Scanner (urlscan.io)
14
+
15
+ Submit any URL for analysis and receive a safety verdict, screenshot, and DNS/HTTP metadata. Uses the [urlscan.io](https://urlscan.io) API.
16
+
17
+ **Requires:** Free `URLSCAN_API_KEY` — sign up at urlscan.io, no credit card needed.
18
+
19
+ ## When to Use
20
+
21
+ - User receives a suspicious link and wants to check it safely
22
+ - User wants to verify if a URL is phishing or malware
23
+ - User asks "is this link safe?" or "scan this URL for threats"
24
+ - Security review of links before sharing them
25
+ - OSINT: see what a URL looks like without visiting it yourself
26
+
27
+ ## How to Use
28
+
29
+ ### Submit a URL for scanning
30
+
31
+ ```powershell
32
+ $url = "https://suspicious-site.example.com"
33
+ $key = $env:URLSCAN_API_KEY
34
+ $body = '{"url": "' + $url + '", "visibility": "unlisted"}'
35
+
36
+ $submit = Invoke-RestMethod -Uri "https://urlscan.io/api/v1/scan/" `
37
+ -Method POST `
38
+ -Headers @{ "API-Key" = $key; "Content-Type" = "application/json" } `
39
+ -Body $body
40
+
41
+ Write-Host "Scan submitted!"
42
+ Write-Host "UUID: $($submit.uuid)"
43
+ Write-Host "Results: $($submit.result)"
44
+ Write-Host "Waiting 30 seconds for scan to complete..."
45
+ Start-Sleep -Seconds 30
46
+ ```
47
+
48
+ ### Retrieve scan results after waiting
49
+
50
+ ```powershell
51
+ $uuid = "PASTE-UUID-FROM-SUBMIT-STEP"
52
+ $key = $env:URLSCAN_API_KEY
53
+
54
+ $result = Invoke-RestMethod -Uri "https://urlscan.io/api/v1/result/$uuid/" `
55
+ -Headers @{ "API-Key" = $key }
56
+
57
+ Write-Host "Final URL: $($result.page.url)"
58
+ Write-Host "IP: $($result.page.ip)"
59
+ Write-Host "Country: $($result.page.country)"
60
+ Write-Host "Malicious: $($result.verdicts.overall.malicious)"
61
+ Write-Host "Phishing: $($result.verdicts.overall.phishing)"
62
+ Write-Host "Score: $($result.verdicts.overall.score)"
63
+ Write-Host "Screenshot: https://urlscan.io/screenshots/$uuid.png"
64
+ ```
65
+
66
+ ### One-shot: submit and wait for result
67
+
68
+ ```powershell
69
+ function Scan-Url {
70
+ param([string]$TargetUrl, [string]$ApiKey = $env:URLSCAN_API_KEY)
71
+
72
+ $body = '{"url": "' + $TargetUrl + '", "visibility": "unlisted"}'
73
+ $submit = Invoke-RestMethod -Uri "https://urlscan.io/api/v1/scan/" `
74
+ -Method POST -Headers @{ "API-Key" = $ApiKey; "Content-Type" = "application/json" } `
75
+ -Body $body
76
+ $uuid = $submit.uuid
77
+
78
+ Write-Host "Scanning $TargetUrl (uuid: $uuid)..."
79
+ Start-Sleep -Seconds 30
80
+
81
+ $result = Invoke-RestMethod -Uri "https://urlscan.io/api/v1/result/$uuid/" `
82
+ -Headers @{ "API-Key" = $ApiKey }
83
+
84
+ [PSCustomObject]@{
85
+ URL = $result.page.url
86
+ IP = $result.page.ip
87
+ Malicious = $result.verdicts.overall.malicious
88
+ Phishing = $result.verdicts.overall.phishing
89
+ Score = $result.verdicts.overall.score
90
+ Report = "https://urlscan.io/result/$uuid/"
91
+ }
92
+ }
93
+
94
+ Scan-Url -TargetUrl "https://example.com"
95
+ ```
96
+
97
+ ## Examples
98
+
99
+ **"Check if this link is safe: https://bit.ly/abc123"**
100
+ → Submit with visibility "unlisted", wait 30 seconds, retrieve result.
101
+
102
+ **"Scan this suspicious email link for phishing"**
103
+ → Use the one-shot function above. If `Phishing = True`, warn the user.
104
+
105
+ **"I want to see what this URL looks like without clicking it"**
106
+ → Submit the scan, then use the screenshot URL: `https://urlscan.io/screenshots/{uuid}.png`
107
+
108
+ ## Cautions
109
+
110
+ - Scans take 20–60 seconds to complete — always wait before fetching results
111
+ - `"visibility": "public"` makes the scan visible to all urlscan.io users — use `"unlisted"` for sensitive URLs
112
+ - Do not scan URLs that contain personal data or credentials in query parameters
113
+ - Free tier allows ~5 scans per minute — add delays for bulk scanning
114
+ - The API key is required to submit scans; public search results are accessible without a key
115
+
116
+ ## Requirements
117
+
118
+ - `URLSCAN_API_KEY` — free account at https://urlscan.io (no credit card required)
@@ -0,0 +1,94 @@
1
+ // skills/urlscan/index.ts
2
+ // Programmatic handler — submits URLs and retrieves verdicts via urlscan.io API.
3
+
4
+ import { ApiSkill, requireApiKey } from '../../core/apiSkillBase'
5
+
6
+ const skill = new ApiSkill({
7
+ name: 'urlscan',
8
+ baseUrl: 'https://urlscan.io/api/v1',
9
+ apiKeyEnv: 'URLSCAN_API_KEY',
10
+ authType: 'header',
11
+ authHeader: 'API-Key',
12
+ rateLimit: { requests: 5, windowMs: 60_000 },
13
+ timeout: 20_000,
14
+ })
15
+
16
+ export type Visibility = 'public' | 'unlisted' | 'private'
17
+
18
+ export interface ScanSubmit {
19
+ uuid: string
20
+ result: string
21
+ api: string
22
+ visibility: string
23
+ message: string
24
+ }
25
+
26
+ export interface ScanVerdict {
27
+ malicious: boolean
28
+ phishing: boolean
29
+ score: number
30
+ tags: string[]
31
+ }
32
+
33
+ export interface ScanResult {
34
+ uuid: string
35
+ url: string
36
+ ip: string
37
+ country: string
38
+ malicious: boolean
39
+ phishing: boolean
40
+ score: number
41
+ reportUrl: string
42
+ screenshot: string
43
+ }
44
+
45
+ /** Submit a URL for scanning. Returns the UUID — call getResult() after ~30s. */
46
+ export async function submitScan(url: string, visibility: Visibility = 'unlisted'): Promise<ScanSubmit> {
47
+ requireApiKey('URLSCAN_API_KEY')
48
+ return skill.post('/scan/', { url, visibility })
49
+ }
50
+
51
+ /** Retrieve a completed scan by UUID. */
52
+ export async function getResult(uuid: string): Promise<ScanResult> {
53
+ requireApiKey('URLSCAN_API_KEY')
54
+
55
+ const raw = await skill.get(`/result/${uuid}/`)
56
+
57
+ return {
58
+ uuid,
59
+ url: raw.page?.url ?? '',
60
+ ip: raw.page?.ip ?? '',
61
+ country: raw.page?.country ?? '',
62
+ malicious: raw.verdicts?.overall?.malicious ?? false,
63
+ phishing: raw.verdicts?.overall?.phishing ?? false,
64
+ score: raw.verdicts?.overall?.score ?? 0,
65
+ reportUrl: `https://urlscan.io/result/${uuid}/`,
66
+ screenshot: `https://urlscan.io/screenshots/${uuid}.png`,
67
+ }
68
+ }
69
+
70
+ /** Submit and poll for result (waits up to maxWaitMs). */
71
+ export async function scanAndWait(
72
+ url: string,
73
+ visibility: Visibility = 'unlisted',
74
+ maxWaitMs = 60_000,
75
+ ): Promise<ScanResult> {
76
+ requireApiKey('URLSCAN_API_KEY')
77
+
78
+ const submit = await submitScan(url, visibility)
79
+ const uuid = submit.uuid
80
+ const poll = 10_000
81
+ const start = Date.now()
82
+
83
+ while (Date.now() - start < maxWaitMs) {
84
+ await new Promise(r => setTimeout(r, poll))
85
+ try {
86
+ return await getResult(uuid)
87
+ } catch (e: any) {
88
+ // 404 means scan is still in progress
89
+ if (!e.message.includes('HTTP 404')) throw e
90
+ }
91
+ }
92
+
93
+ throw new Error(`urlscan: scan ${uuid} did not complete within ${maxWaitMs / 1000}s`)
94
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "urlscan",
3
+ "version": "1.0.0",
4
+ "description": "Submit URLs for automated malware and phishing analysis, then retrieve safety verdicts and screenshots via urlscan.io",
5
+ "author": "aiden",
6
+ "license": "MIT",
7
+ "tools": [],
8
+ "trigger_phrases": [],
9
+ "compatible_agents": [
10
+ "aiden"
11
+ ],
12
+ "min_agent_version": "3.0.0",
13
+ "tags": [
14
+ "security",
15
+ "malware",
16
+ "phishing",
17
+ "url",
18
+ "scan",
19
+ "osint",
20
+ "urlscan",
21
+ "threat-intel"
22
+ ],
23
+ "created": "2026-04-27T17:11:41.053Z"
24
+ }
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: virustotal
3
+ description: Check file hashes, URLs, domains, and IP addresses against 70+ antivirus engines and threat intelligence feeds via VirusTotal
4
+ category: security
5
+ version: 1.0.0
6
+ license: Apache-2.0
7
+ origin: aiden
8
+ tags: security, virustotal, malware, antivirus, threat-intel, hash, url, domain, ip, incident-response
9
+ env_required:
10
+ - VIRUSTOTAL_API_KEY
11
+ ---
12
+
13
+ # VirusTotal — Threat Intelligence
14
+
15
+ VirusTotal aggregates results from 70+ antivirus engines and threat intelligence feeds. Check file hashes, URLs, domains, and IP addresses for known malware, phishing, and suspicious activity.
16
+
17
+ **Requires:** `VIRUSTOTAL_API_KEY` — free at https://www.virustotal.com/gui/my-apikey (4 req/min on free tier).
18
+
19
+ ## When to Use
20
+
21
+ - User has a file hash and wants to know if it is malware
22
+ - User has a suspicious URL and wants a reputation check
23
+ - User is investigating an incident and needs threat context for a domain or IP
24
+ - User asks "is this hash malicious?", "check this URL", or "is this domain flagged?"
25
+ - SOC/IR workflow: triage IOCs (indicators of compromise) quickly
26
+
27
+ ## How to Use
28
+
29
+ ### Check a file hash (MD5, SHA-1, or SHA-256)
30
+
31
+ ```powershell
32
+ $hash = "44d88612fea8a8f36de82e1278abb02f" # EICAR test file
33
+ $key = $env:VIRUSTOTAL_API_KEY
34
+ $url = "https://www.virustotal.com/api/v3/files/$hash"
35
+
36
+ $result = Invoke-RestMethod -Uri $url -Headers @{ "x-apikey" = $key }
37
+ $stats = $result.data.attributes.last_analysis_stats
38
+ Write-Host "File: $($result.data.attributes.meaningful_name)"
39
+ Write-Host "Malicious: $($stats.malicious) / $($stats.malicious + $stats.undetected + $stats.harmless)"
40
+ Write-Host "Suspicious: $($stats.suspicious)"
41
+ Write-Host "First seen: $($result.data.attributes.first_submission_date)"
42
+ ```
43
+
44
+ ### Check a URL for malware or phishing
45
+
46
+ ```powershell
47
+ $targetUrl = "https://example.com"
48
+ $key = $env:VIRUSTOTAL_API_KEY
49
+
50
+ # URL id = URL-safe base64 without padding
51
+ $bytes = [System.Text.Encoding]::UTF8.GetBytes($targetUrl)
52
+ $b64 = [Convert]::ToBase64String($bytes).Replace('+','-').Replace('/','_').TrimEnd('=')
53
+ $url = "https://www.virustotal.com/api/v3/urls/$b64"
54
+
55
+ $result = Invoke-RestMethod -Uri $url -Headers @{ "x-apikey" = $key }
56
+ $stats = $result.data.attributes.last_analysis_stats
57
+ Write-Host "URL: $targetUrl"
58
+ Write-Host "Malicious: $($stats.malicious)"
59
+ Write-Host "Phishing: $($result.data.attributes.categories -join ', ')"
60
+ Write-Host "Reputation: $($result.data.attributes.reputation)"
61
+ ```
62
+
63
+ ### Check a domain's reputation
64
+
65
+ ```powershell
66
+ $domain = "example.com"
67
+ $key = $env:VIRUSTOTAL_API_KEY
68
+ $url = "https://www.virustotal.com/api/v3/domains/$domain"
69
+
70
+ $result = Invoke-RestMethod -Uri $url -Headers @{ "x-apikey" = $key }
71
+ $attrs = $result.data.attributes
72
+ Write-Host "Domain: $domain"
73
+ Write-Host "Reputation: $($attrs.reputation)"
74
+ Write-Host "Categories: $($attrs.categories.PSObject.Properties.Value -join ', ')"
75
+ $stats = $attrs.last_analysis_stats
76
+ Write-Host "Malicious: $($stats.malicious) engines"
77
+ ```
78
+
79
+ ### Check an IP address
80
+
81
+ ```powershell
82
+ $ip = "1.1.1.1"
83
+ $key = $env:VIRUSTOTAL_API_KEY
84
+ $url = "https://www.virustotal.com/api/v3/ip_addresses/$ip"
85
+
86
+ $result = Invoke-RestMethod -Uri $url -Headers @{ "x-apikey" = $key }
87
+ $attrs = $result.data.attributes
88
+ $stats = $attrs.last_analysis_stats
89
+ Write-Host "IP: $ip"
90
+ Write-Host "AS Owner: $($attrs.as_owner)"
91
+ Write-Host "Country: $($attrs.country)"
92
+ Write-Host "Reputation: $($attrs.reputation)"
93
+ Write-Host "Malicious: $($stats.malicious) engines"
94
+ ```
95
+
96
+ ## Examples
97
+
98
+ **"Is hash 44d88612fea8a8f36de82e1278abb02f malware?"**
99
+ → Use the file hash check — malicious count > 0 means confirmed threats.
100
+
101
+ **"Check if https://suspicious-login.com is phishing"**
102
+ → Use the URL check — look at `malicious` and `phishing` categories.
103
+
104
+ **"I got an alert for domain evil-c2.net — is it known bad?"**
105
+ → Use the domain check — look at reputation score and malicious engine count.
106
+
107
+ **"This IP keeps hitting our firewall — is it a known attacker?"**
108
+ → Use the IP check — `reputation < 0` and high malicious count = treat as threat.
109
+
110
+ ## Cautions
111
+
112
+ - Free tier rate limit: 4 requests per minute — add `Start-Sleep -Seconds 16` between calls when checking multiple IOCs
113
+ - VirusTotal results reflect last scan time; for fresh analysis you must submit (POST /files or POST /urls) — not covered here
114
+ - A clean result (0 detections) does not guarantee safety — new malware may not yet be in the database
115
+ - File hashes are public — never submit sensitive files directly; only submit the hash
116
+ - Reputation score: positive = clean, 0 = neutral, negative = suspicious/malicious
117
+
118
+ ## Requirements
119
+
120
+ - `VIRUSTOTAL_API_KEY` — free account at https://www.virustotal.com/gui/join-us
@@ -0,0 +1,124 @@
1
+ // skills/virustotal/index.ts
2
+ // Programmatic handler — file, URL, domain, and IP reputation via VirusTotal v3 API.
3
+
4
+ import { ApiSkill, requireApiKey } from '../../core/apiSkillBase'
5
+
6
+ const skill = new ApiSkill({
7
+ name: 'virustotal',
8
+ baseUrl: 'https://www.virustotal.com/api/v3',
9
+ apiKeyEnv: 'VIRUSTOTAL_API_KEY',
10
+ authType: 'header',
11
+ authHeader: 'x-apikey',
12
+ rateLimit: { requests: 4, windowMs: 60_000 },
13
+ timeout: 20_000,
14
+ retries: 3,
15
+ })
16
+
17
+ export interface AnalysisStats {
18
+ malicious: number
19
+ suspicious: number
20
+ undetected: number
21
+ harmless: number
22
+ timeout: number
23
+ }
24
+
25
+ export interface VTReport {
26
+ id: string
27
+ name: string
28
+ reputation: number
29
+ stats: AnalysisStats
30
+ categories: string[]
31
+ lastAnalysis: string
32
+ }
33
+
34
+ /** Encode a URL to the VirusTotal URL identifier (URL-safe base64, no padding). */
35
+ function urlId(url: string): string {
36
+ return Buffer.from(url).toString('base64url')
37
+ }
38
+
39
+ function extractStats(attrs: any): AnalysisStats {
40
+ const s = attrs?.last_analysis_stats ?? {}
41
+ return {
42
+ malicious: s.malicious ?? 0,
43
+ suspicious: s.suspicious ?? 0,
44
+ undetected: s.undetected ?? 0,
45
+ harmless: s.harmless ?? 0,
46
+ timeout: s.timeout ?? 0,
47
+ }
48
+ }
49
+
50
+ function extractCategories(attrs: any): string[] {
51
+ if (!attrs?.categories) return []
52
+ if (Array.isArray(attrs.categories)) return attrs.categories as string[]
53
+ // categories can be an object of { engine: category } — return unique values
54
+ return [...new Set(Object.values(attrs.categories) as string[])]
55
+ }
56
+
57
+ /** Check a file hash (MD5, SHA-1, or SHA-256) against VirusTotal. */
58
+ export async function fileReport(hash: string): Promise<VTReport> {
59
+ requireApiKey('VIRUSTOTAL_API_KEY')
60
+
61
+ const raw = await skill.get(`/files/${encodeURIComponent(hash)}`)
62
+ const attrs = raw.data?.attributes ?? {}
63
+
64
+ return {
65
+ id: raw.data?.id ?? hash,
66
+ name: attrs.meaningful_name ?? attrs.name ?? hash,
67
+ reputation: attrs.reputation ?? 0,
68
+ stats: extractStats(attrs),
69
+ categories: extractCategories(attrs),
70
+ lastAnalysis: attrs.last_analysis_date ?? '',
71
+ }
72
+ }
73
+
74
+ /** Check a URL's reputation via VirusTotal. */
75
+ export async function urlReport(url: string): Promise<VTReport> {
76
+ requireApiKey('VIRUSTOTAL_API_KEY')
77
+
78
+ const id = urlId(url)
79
+ const raw = await skill.get(`/urls/${id}`)
80
+ const attrs = raw.data?.attributes ?? {}
81
+
82
+ return {
83
+ id,
84
+ name: url,
85
+ reputation: attrs.reputation ?? 0,
86
+ stats: extractStats(attrs),
87
+ categories: extractCategories(attrs),
88
+ lastAnalysis: attrs.last_analysis_date ?? '',
89
+ }
90
+ }
91
+
92
+ /** Check a domain's reputation via VirusTotal. */
93
+ export async function domainReport(domain: string): Promise<VTReport> {
94
+ requireApiKey('VIRUSTOTAL_API_KEY')
95
+
96
+ const raw = await skill.get(`/domains/${encodeURIComponent(domain)}`)
97
+ const attrs = raw.data?.attributes ?? {}
98
+
99
+ return {
100
+ id: raw.data?.id ?? domain,
101
+ name: domain,
102
+ reputation: attrs.reputation ?? 0,
103
+ stats: extractStats(attrs),
104
+ categories: extractCategories(attrs),
105
+ lastAnalysis: attrs.last_analysis_date ?? '',
106
+ }
107
+ }
108
+
109
+ /** Check an IP address reputation via VirusTotal. */
110
+ export async function ipReport(ip: string): Promise<VTReport> {
111
+ requireApiKey('VIRUSTOTAL_API_KEY')
112
+
113
+ const raw = await skill.get(`/ip_addresses/${encodeURIComponent(ip)}`)
114
+ const attrs = raw.data?.attributes ?? {}
115
+
116
+ return {
117
+ id: raw.data?.id ?? ip,
118
+ name: ip,
119
+ reputation: attrs.reputation ?? 0,
120
+ stats: extractStats(attrs),
121
+ categories: extractCategories(attrs),
122
+ lastAnalysis: attrs.last_analysis_date ?? '',
123
+ }
124
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "virustotal",
3
+ "version": "1.0.0",
4
+ "description": "Check file hashes, URLs, domains, and IP addresses against 70+ antivirus engines and threat intelligence feeds via VirusTotal",
5
+ "author": "aiden",
6
+ "license": "MIT",
7
+ "tools": [],
8
+ "trigger_phrases": [],
9
+ "compatible_agents": [
10
+ "aiden"
11
+ ],
12
+ "min_agent_version": "3.0.0",
13
+ "tags": [
14
+ "security",
15
+ "virustotal",
16
+ "malware",
17
+ "antivirus",
18
+ "threat-intel",
19
+ "hash",
20
+ "url",
21
+ "domain",
22
+ "ip",
23
+ "incident-response"
24
+ ],
25
+ "created": "2026-04-27T17:11:41.082Z"
26
+ }