Compare commits

...

20 Commits

Author SHA1 Message Date
83fab5dbd3 1.0.12 2023-08-01 15:05:26 +02:00
174ba7f153 fix(core): update 2023-08-01 15:05:25 +02:00
be520e9dac 1.0.11 2023-08-01 13:02:05 +02:00
fd9211393b fix(core): update 2023-08-01 13:02:04 +02:00
5abf3bc0aa 1.0.10 2023-08-01 13:01:48 +02:00
9eb81694c7 fix(core): update 2023-08-01 13:01:48 +02:00
4978d56330 1.0.9 2023-08-01 12:49:59 +02:00
c13549ac82 fix(core): update 2023-08-01 12:49:59 +02:00
95bf41a602 1.0.8 2023-07-28 14:25:03 +02:00
cffab3d66d fix(core): update 2023-07-28 14:25:03 +02:00
63e7fb2140 1.0.7 2023-07-28 14:13:17 +02:00
792224c96b fix(core): update 2023-07-28 14:13:16 +02:00
0517f95d25 1.0.6 2023-07-28 09:55:42 +02:00
e0aa3b9b55 fix(core): update 2023-07-28 09:55:41 +02:00
b7e4efef6c 1.0.5 2023-07-28 09:54:23 +02:00
ca1d0bd9a3 fix(core): update 2023-07-28 09:54:22 +02:00
b80e042cbd 1.0.4 2023-07-28 07:33:55 +02:00
bf12f0d750 fix(core): update 2023-07-28 07:33:54 +02:00
e6d87b648a 1.0.3 2023-07-28 07:32:00 +02:00
429df4c0d1 fix(core): update 2023-07-28 07:31:59 +02:00
12 changed files with 474 additions and 155 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@apiclient.xyz/abuse.ch",
"version": "1.0.2",
"version": "1.0.12",
"private": false,
"description": "an unofficial client to retrieve abuse.ch data",
"main": "dist_ts/index.js",
@ -36,5 +36,13 @@
"cli.js",
"npmextra.json",
"readme.md"
]
],
"dependencies": {
"@push.rocks/smartfile": "^10.0.28",
"@push.rocks/smartpath": "^5.0.5",
"csv-parser": "^3.0.0",
"https-proxy-agent": "^7.0.1",
"node-fetch": "^3.3.2",
"unzipper": "^0.10.14"
}
}

326
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,23 @@
import { expect, expectAsync, tap } from '@push.rocks/tapbundle';
import * as abuseCh from '../ts/index.js';
tap.test('first test', async () => {
console.log(abuseCh);
tap.test('should deal with UrlHouse data', async () => {
const urlHouse = new abuseCh.UrlHouse();
const data = await urlHouse.getData();
console.log(data.length);
});
tap.test('should deal with UrlHouse data', async () => {
const threatFox = new abuseCh.ThreatFox();
const data = await threatFox.getData();
console.log(data.length);
});
tap.test('should deal with FeodoTracker data', async () => {
const feodoTracker = new abuseCh.FeodoTracker();
const data = await feodoTracker.getData();
console.log(data.length);
console.log(data[1]);
});
tap.start();

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@apiclient.xyz/abuse.ch',
version: '1.0.2',
version: '1.0.12',
description: 'an unofficial client to retrieve abuse.ch data'
}

View File

@ -0,0 +1,52 @@
import * as plugins from './plugins.js';
import * as paths from './paths.js';
import * as helpers from './helpers.js';
export interface IFeodoTrackerData {
ip_address: string;
port: number;
status: string;
hostname: string | null;
as_number: number;
as_name: string;
country: string;
first_seen: string;
last_online: string;
malware: string;
}
export class FeodoTracker {
private static readonly FEODO_TRACKER_API_URL: string = 'https://feodotracker.abuse.ch/downloads/ipblocklist.json';
public async getData(): Promise<IFeodoTrackerData[]> {
const response = await plugins.nodeFetch(FeodoTracker.FEODO_TRACKER_API_URL, {
...(helpers.findProxy() ? {
agent: helpers.getAgent(),
} : {})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: IFeodoTrackerData[] = await response.json() as IFeodoTrackerData[];
// Ensure the data is an array and has the expected structure
if (!Array.isArray(data) || !data.every(item =>
typeof item.ip_address === 'string' &&
typeof item.port === 'number' &&
(typeof item.hostname === 'string' || item.hostname === null) &&
typeof item.as_number === 'number' &&
typeof item.as_name === 'string' &&
typeof item.country === 'string' &&
typeof item.first_seen === 'string' &&
typeof item.last_online === 'string' &&
typeof item.malware === 'string'
)) {
throw new Error(`Invalid data structure!`);
}
return data;
}
}

View File

@ -0,0 +1,81 @@
import * as plugins from './plugins.js';
import * as paths from './paths.js';
import * as helpers from './helpers.js';
export interface IThreatFoxData {
ID: string;
Dateadded: string;
URL: string;
URLStatus: string;
Threat: string;
AssociatedTags: string;
ThreatFoxLink: string;
Reporter: string;
}
export class ThreatFox {
private static readonly THREATFOX_API_URL: string = 'https://threatfox.abuse.ch/export/csv/full'; // Replace with actual API endpoint
public async getData(): Promise<IThreatFoxData[]> {
plugins.smartfile.fs.ensureDirSync(paths.threatFoxTmp);
const zipPath = plugins.path.join(paths.threatFoxTmp, 'threatfox.zip');
const csvPath = plugins.path.join(paths.threatFoxTmp, 'full.csv');
const response = await plugins.nodeFetch(ThreatFox.THREATFOX_API_URL, {
...(helpers.findProxy() ? {
agent: helpers.getAgent(),
} : {})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
await new Promise((resolve, reject) => {
const fileStream = plugins.fs.createWriteStream(zipPath);
// @ts-ignore
const readable = plugins.stream.Readable.from(response.body);
plugins.stream.pipeline(readable, fileStream, (err) => {
if (err) reject(err);
else resolve(null);
});
});
await new Promise((resolve, reject) => {
plugins.stream.pipeline(
plugins.fs.createReadStream(zipPath),
plugins.unzipper.Extract({ path: paths.threatFoxTmp }),
(err) => {
if (err) reject(err);
else resolve(null);
}
);
});
let data: IThreatFoxData[] = [];
await new Promise((resolve, reject) => {
plugins.stream.pipeline(
plugins.fs.createReadStream(csvPath),
plugins.csv({
headers: ['ID', 'Dateadded', 'URL', 'URLStatus', 'Threat', 'AssociatedTags', 'ThreatFoxLink', 'Reporter'],
mapValues: ({ header, value }) => value.trim()
}),
(err) => {
if (err) reject(err);
}
)
.on('data', (row) => {
data.push(row);
})
.on('end', resolve)
.on('error', reject);
});
data = data.map((item) => {
return {
...item,
URL: item.URL?.replace('http', 'ht-NOCLICK-NOLINK-tp'),
}
});
plugins.smartfile.fs.removeSync(paths.threatFoxTmp);
return data;
}
}

View File

@ -0,0 +1,81 @@
import * as plugins from './plugins.js';
import * as paths from './paths.js';
import * as helpers from './helpers.js';
export interface IUrlHouseData {
ID: string;
Dateadded: string;
URL: string;
URLStatus: string;
Threat: string;
AssociatedTags: string;
UrlHausLink: string;
Reporter: string;
}
export class UrlHouse {
private static readonly URLHOUSE_API_URL: string = 'https://urlhaus.abuse.ch/downloads/csv/';
public async getData(): Promise<IUrlHouseData[]> {
plugins.smartfile.fs.ensureDirSync(paths.urlHouseTmp);
const zipPath = plugins.path.join(paths.urlHouseTmp, 'urlhaus.zip');
const csvPath = plugins.path.join(paths.urlHouseTmp, 'csv.txt');
const response = await plugins.nodeFetch(UrlHouse.URLHOUSE_API_URL, {
...(helpers.findProxy() ? {
agent: helpers.getAgent(),
} : {})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
await new Promise((resolve, reject) => {
const fileStream = plugins.fs.createWriteStream(zipPath);
// @ts-ignore
const readable = plugins.stream.Readable.from(response.body);
plugins.stream.pipeline(readable, fileStream, (err) => {
if (err) reject(err);
else resolve(null);
});
});
await new Promise((resolve, reject) => {
plugins.stream.pipeline(
plugins.fs.createReadStream(zipPath),
plugins.unzipper.Extract({ path: paths.urlHouseTmp }),
(err) => {
if (err) reject(err);
else resolve(null);
}
);
});
let data: IUrlHouseData[] = [];
await new Promise((resolve, reject) => {
plugins.stream.pipeline(
plugins.fs.createReadStream(csvPath),
plugins.csv({
headers: ['ID', 'Dateadded', 'URL', 'URLStatus', 'Threat', 'AssociatedTags', 'UrlHausLink', 'Reporter'],
mapValues: ({ header, value }) => value.trim()
}),
(err) => {
if (err) reject(err);
}
)
.on('data', (row) => {
data.push(row);
})
.on('end', resolve)
.on('error', reject);
});
data = data.map((item) => {
return {
...item,
URL: item.URL?.replace('http', 'ht-NOCLICK-NOLINK-tp'),
}
});
plugins.smartfile.fs.removeSync(paths.urlHouseTmp);
return data;
}
}

View File

8
ts/helpers.ts Normal file
View File

@ -0,0 +1,8 @@
import * as plugins from './plugins.js';
export const findProxy = () => {
return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy
}
export const getAgent = () => {
return new plugins.httpsProxy.HttpsProxyAgent(findProxy());
}

View File

@ -1,3 +1,3 @@
import * as plugins from './abuse.ch.plugins.js';
export let demoExport = 'Hi there! :) This is an exported string';
export * from './abuse.ch.classes.feodotracker.js';
export * from './abuse.ch.classes.threatfox.js';
export * from './abuse.ch.classes.urlhouse.js';

8
ts/paths.ts Normal file
View File

@ -0,0 +1,8 @@
import * as plugins from './plugins.js';
export const packageDir = plugins.path.join(plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), '../');
export const nogitDir = plugins.path.join(packageDir, '.nogit');
export const urlHouseTmp = plugins.path.join(nogitDir, 'tmp.urlhaus');
export const threatFoxTmp = plugins.path.join(nogitDir, 'tmp.threatfox');
export const feodoTrackerTmp = plugins.path.join(nogitDir, 'tmp.feodotracker');

34
ts/plugins.ts Normal file
View File

@ -0,0 +1,34 @@
// node native
import * as stream from 'stream';
import * as util from 'util';
import unzipper from 'unzipper';
import csv from 'csv-parser';
import * as fs from 'fs';
import * as path from 'path';
export {
stream,
util,
unzipper,
csv,
fs,
path,
}
// @push.rocks scope
import * as smartfile from '@push.rocks/smartfile';
import * as smartpath from '@push.rocks/smartpath';
export {
smartfile,
smartpath,
}
// third party
import nodeFetch from 'node-fetch';
import * as httpsProxy from 'https-proxy-agent';
export {
nodeFetch,
httpsProxy,
}