Skip to content

Commit

Permalink
feat: implement Contact to replace @nodesecure/authors (#266)
Browse files Browse the repository at this point in the history
  • Loading branch information
fraxken committed Jul 15, 2024
1 parent 54450b0 commit 35b865e
Show file tree
Hide file tree
Showing 28 changed files with 817 additions and 95 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ interface Options {
location: string;
};

highlight?: {
contacts: Contact[];
};

/**
* Include project devDependencies (only available for cwd command)
*
Expand Down Expand Up @@ -133,6 +137,7 @@ Click on one of the links to access the documentation of the workspace:
| tarball | [@nodesecure/sec-literal](./workspaces/tarball) |
| tree-walker | [@nodesecure/tree-walker](./workspaces/tree-walker) |
| mama | [@nodesecure/mama](./workspaces/mama) |
| contact | [@nodesecure/contact](./workspaces/contact) |
| conformance | [@nodesecure/npm-types](./workspaces/conformance) |
| npm-types | [@nodesecure/npm-types](./workspaces/npm-types) |

Expand Down
10 changes: 5 additions & 5 deletions docs/from.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,18 @@ await hydratePayloadDependencies(dependencies, {
For more information on how to create a strategy and how they operate, please [read the following documentation](https://github.com/NodeSecure/vulnera/blob/main/docs/adding_new_strategy.md).
## Steps 4: Get warnings and flagged authors
## Steps 4: Get warnings and illuminated contacts
In this step, we look for packages or authors potentially identified as problematic (by the person requesting the analysis or us).
```js
const { warnings, flaggedAuthors } = await getDependenciesWarnings(dependencies);
const { warnings, illuminateds } = await getDependenciesWarnings(dependencies);
payload.warnings = warnings;
payload.flaggedAuthors = flaggedAuthors;
payload.highlighted = {
contacts: illuminateds
};
```
> Under the hood we use our newest package [@nodesecure/authors](https://github.com/NodeSecure/authors) to optimize and flag authors.
By default we show warnings for the following two packages:
- @scarf/scarf
- iohook
Expand Down
43 changes: 31 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"workspaces/mama",
"workspaces/tree-walker",
"workspaces/conformance",
"workspaces/contact",
"workspaces/npm-types"
],
"scripts": {
Expand Down
3 changes: 3 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
{
"path": "./workspaces/mama"
},
{
"path": "./workspaces/contact"
},
{
"path": "./workspaces/tarball"
},
Expand Down
119 changes: 119 additions & 0 deletions workspaces/contact/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<p align="center"><h1 align="center">
@nodesecure/contact
</h1>

<p align="center">
Utilities to extract/fetch data on NPM contacts (author, maintainers etc..)
</p>

## Requirements
- [Node.js](https://nodejs.org/en/) v20 or higher

## Getting Started

This package is available in the Node Package Repository and can be easily installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or [yarn](https://yarnpkg.com).

```bash
$ npm i @nodesecure/contact
# or
$ yarn add @nodesecure/contact
```

## Usage example

Here is an example of usage from the Scanner. In this case, we are using **dependenciesMap**, which is a `Record<string, Dependency>`. However, you can build your own record of `ContactExtractorPackageMetadata`.

```ts
import {
ContactExtractor,
type ContactExtractorPackageMetadata
} from "@nodesecure/contact";

const dependencies: Record<string, ContactExtractorPackageMetadata> = Object.create(null);
for (const [packageName, dependency] of dependenciesMap) {
const { author, maintainers } = dependency.metadata;

dependencies[packageName] = {
maintainers,
...( author === null ? {} : { author } )
}
}

const extractor = new ContactExtractor({
highlight: [
{
name: "Sindre Sorhus"
}
]
});
const contacts = extractor.fromDependencies(
dependencies
);
console.log(contacts);
```

## API

Contact is defined by the following TypeScript interface:
```ts
interface Contact {
email?: string;
url?: string;
name: string;
}
```

> [!NOTE]
> This package authorizes literal RegExp in the name property
### ContactExtractor

The constructor take a list of contacts you want to find/extract.

```ts
interface ContactExtractorOptions {
highlight: Contact[];
}
```

The method **fromDependencies** will return an array of IlluminatedContact objects if any are found in the provided dependencies.

```ts
type IlluminatedContact = Contact & {
dependencies: string[];
}
```
### compareContact(contactA: Contact, contactB: Contact, options?: CompareOptions): boolean
Compare two contacts and return `true` if they are the same person
```ts
import {
compareContact
} from "@nodesecure/contact";
import assert from "node:assert";

assert.ok(
compareContact(
{ name: "john doe" },
{ name: "John Doe" }
)
);
```

Each string is trimmed, converted to lowercase, and any multiple spaces are reduced to a single space.

#### Options

```ts
interface CompareOptions {
/**
* @default true
*/
compareName?: boolean;
}
```

## License
MIT
38 changes: 38 additions & 0 deletions workspaces/contact/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@nodesecure/contact",
"version": "1.0.0",
"description": "Utilities to extract/fetch data on NPM contacts (author, maintainers ..)",
"type": "module",
"exports": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"node": ">=20"
},
"scripts": {
"build": "tsc -b",
"prepublishOnly": "npm run build",
"test-only": "glob -c \"tsx --test\" \"./test/**/*.spec.ts\"",
"test": "c8 -r html npm run test-only"
},
"files": [
"dist"
],
"keywords": [
"author",
"contact",
"maintainer"
],
"author": "GENTILHOMME Thomas <gentilhomme.thomas@gmail.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/NodeSecure/scanner.git"
},
"bugs": {
"url": "https://github.com/NodeSecure/scanner/issues"
},
"homepage": "https://github.com/NodeSecure/tree/master/workspaces/contact#readme",
"devDependencies": {
"@faker-js/faker": "^8.4.1"
}
}
61 changes: 61 additions & 0 deletions workspaces/contact/src/ContactExtractor.class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Import Third-party Dependencies
import type { Contact } from "@nodesecure/npm-types";

// Import Internal Dependencies
import {
UnlitContact,
type IlluminatedContact
} from "./UnlitContact.class.js";

export type { IlluminatedContact };

export interface ContactExtractorPackageMetadata {
author?: Contact;
maintainers: Contact[];
}

export interface ContactExtractorOptions {
highlight: Contact[];
}

export class ContactExtractor {
private highlighted: Contact[] = [];

constructor(
options: ContactExtractorOptions
) {
const { highlight } = options;

this.highlighted = structuredClone(highlight);
}

fromDependencies(
dependencies: Record<string, ContactExtractorPackageMetadata>
): IlluminatedContact[] {
const unlitContacts = this.highlighted
.map((contact) => new UnlitContact(contact));

for (const [packageName, metadata] of Object.entries(dependencies)) {
for (const unlit of unlitContacts) {
const isMaintainer = extractMetadataContacts(metadata)
.some((contact) => unlit.compareTo(contact));
if (isMaintainer) {
unlit.dependencies.add(packageName);
}
}
}

return unlitContacts.flatMap(
(unlit) => unlit.dependencies.size > 0 ? [unlit.illuminate()] : []
);
}
}

function extractMetadataContacts(
metadata: ContactExtractorPackageMetadata
): Contact[] {
return [
...(metadata.author ? [metadata.author] : []),
...metadata.maintainers
];
}
Loading

0 comments on commit 35b865e

Please sign in to comment.