How to compute supported cryptography algorithms? - node.js

Background
I support an automated testing library for my company, and it has a thin wrapper around the sftp-promises class SFTPClient. Sometime in the recent past ssh2 (a dependency of sftp-promises) required that the caller provide a list of supported OpenSSL properties. I found an answer on StackOverflow at the time, copy-pasted the configuration, and deleted items that threw errors until it ran. I'm no longer the sole owner of the project, and the project is migrating to a cloud provider, so I want to improve this configuration to detect supported algorithms.
Question
Using Node.js, how can I determine the values for the algorithms configuration property of the SFTPClient class provided by sftp-promises?
I can easily see that the property config.algorithms.cipher can be derived from the following:
import { getCiphers } from 'node:crypto';
import { IAlgorithm } from '../interfaces';
const algorithms: IAlgorithms = {
cipher: getCiphers()
};
However, this leaves compress, hmac, kex, and serverHostKey as properties I am hard-coding, and I'm not sure how I can derive these values at build-time, and more importantly at run-time since this will be running on other machines.
Code
Here is an edited version of the code
import SFTPClient from 'sftp-promises';
import { IFtpClientConfig, IFtpClientConfigAlgorithms } from '../interfaces';
export class FtpClient {
private lastLocalFileName?: string;
private lastLocalFilePath: string;
private lastRemoteFileName?: string;
private lastRemotePath?: string;
constructor(config: IFtpClientConfig, downloadDir: string) {
config.algorithms ||= FtpClient.algorithms;
this.client = new SFTPClient(config);
this.lastLocalFilePath = downloadDir;
}
static get algorithms(): IFtpClientConfigAlgorithms {
return {
kex: [
'diffie-hellman-group1-sha1',
'ecdh-sha2-nistp256',
'ecdh-sha2-nistp384',
'ecdh-sha2-nistp521',
'diffie-hellman-group-exchange-sha256',
'diffie-hellman-group14-sha1'
],
cipher: [
'3des-cbc',
'aes128-ctr',
'aes192-ctr',
'aes256-ctr',
'aes128-gcm#openssh.com',
'aes256-gcm#openssh.com'
],
serverHostKey: ['ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521'],
hmac: ['hmac-sha2-256', 'hmac-sha2-512', 'hmac-sha1']
};
}
}
And the backing interfaces to understand the shape of the data:
// interfaces/ftp-client/index.ts
export { IAlgorithms as IFtpClientConfigAlgorithms } from './IAlgorithms';
export {
IAuthHandler as IFtpClientConfigAuthHandler,
IAuthHandlerAgent as IFtpClientConfigAuthHandlerAgent,
IAuthHandlerCallback as IFtpClientConfigAuthHandlerCallback,
IAuthHandlerHostBased as IFtpClientConfigAuthHandlerHostBased,
IAuthHandlerInteractive as IFtpClientConfigAuthHandlerInteractive,
IAuthHandlerNone as IFtpClientConfigAuthHandlerNone,
IAuthHandlerObject as IFtpClientConfigAuthHandlerObject,
IAuthHandlerPassword as IFtpClientConfigAuthHandlerPassword,
IAuthHandlerPublicKey as IFtpClientConfigAuthHandlerPublicKey,
IAuthHandlerString as IFtpClientConfigAuthHandlerString
} from './IAuthHandler';
export { IConfig as IFtpClientConfig } from './IConfig';
// interfaces/ftp-client/IAlgorithms.ts
export interface IAlgorithms {
cipher?: string[];
compress?: string[];
hmac?: string[];
kex?: string[];
serverHostKey?: string[];
}
// interfaces/ftp-client/IConfig.ts
import { BaseAgent } from 'ssh2';
import { IAlgorithms } from './IAlgorithms';
import { IAuthHandler } from './IAuthHandler';
export interface IConfig {
agent?: string | BaseAgent;
agentFwd?: boolean;
algorithms?: IAlgorithms;
authHandler?: IAuthHandler;
debug?: boolean;
forceIPv4?: boolean;
forceIPv6?: boolean;
host?: string;
hostHashAlgo?: string;
hostHashCb?: CallableFunction;
keepaliveCountMax?: number;
keepaliveInterval?: number;
localAddress?: string;
localPort?: string | number;
password?: string;
port?: string | number;
privateKey?: string | Buffer;
readyTimeout?: number;
strictVendor?: unknown;
tryKeyboard?: boolean;
username?: string;
}

Related

TypeORM Migration Generation Fails With With Error "Package subpath '' is not defined by "exports""

I have, among others, a Product entity defined as follows:
#Entity()
export class Product extends BaseEntity {
#Column()
public name: string;
#Column()
public category: ProductCategory;
#Column({ type: 'json', nullable: true })
public additionalInfo?: ExchangeInfo | CoinInfo;
#OneToMany(() => Review, (review) => review.product)
public reviews: Review[];
}
export enum ProductCategory {
Exchange,
Coin,
}
export interface ExchangeInfo {
company: string;
description: string;
}
export interface CoinInfo {
description: string;
url: string;
}
I generate the first migration (on empty db) as follows:
npm run migration:generate src/migrations/InitialMigration
Everything runs smoothly and my database is populated with the tables as expected.
After this initial migration I create a new, empty one which I use to seed data as follows:
npm run migration:create src/migrations/SeedMigration
and the resulting migration runs smoothly:
export class SeedMigration1674664545529 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
...
await queryRunner.manager.save(allItems, { chunk: 100 });
}
public async down(queryRunner: QueryRunner): Promise<void> {
...
await queryRunner.manager.remove(products);
}
}
Finally, I change any entity and try to generate a new migration to reflect these changes:
npm run migration:generate src/migrations/EntityChanged
Here is where I get the following error:
**Package subpath './product.entity' is not defined by "exports" in /../api/node_modules/entities/package.json**
What could this be?
The only thing I notice is that the file that triggers the error is Product.entity.ts, which unlike other entities has additional interfaced defined in the file but not sure how that would be relevant.

Two validators for one single entity in DTO

Are there any ways or would it be possible to have two validator in one single entity? Like for the given example code below, the identifier would accept an email as its payload but it would also accept
number/mobile number as its payload as well.
#ApiProperty()
#IsString()
#IsNotEmpty()
#IsEmail()
identifier: string;
EDIT:
I have tried,
#ApiProperty()
#IsString()
#IsNotEmpty()
#IsEmail()
#IsPhoneNumber('US')
identifier: string;
But it does not work.
EDIT 2:
I found a reference code based on this previous thread, How to use else condition in validationif decorator nestjs class-validator?, and I copied his validation class.
import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from "class-validator";
import { IdentifierType } from "../interface/access.interface";
#ValidatorConstraint({ name: 'IdentifierValidation', async: false })
export class IdentifierValidation implements ValidatorConstraintInterface {
validate(identifier: string, args: ValidationArguments) {
if (JSON.parse(JSON.stringify(args.object)).type === IdentifierType.MOBILE) {
var regexp = new RegExp('/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im');
// "regexp" variable now validate phone number.
return regexp.test(identifier);
} else {
regexp = new RegExp("^[a-zA-Z0-9_.+-]+#[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$");
// "regexp" variable now validate email address.
return regexp.test(identifier);
}
}
defaultMessage(args: ValidationArguments) {
if (JSON.parse(JSON.stringify(args.object)).type === IdentifierType.MOBILE) {
return 'Enter a valid phone number.'
} else {
return 'Enter a valid email address.'
}
}
}
DTO -
export class VerifyOtpDto {
#Validate(IdentifierValidation)
#ApiProperty()
#IsNotEmpty()
identifier: string;
#ApiProperty({ enum: IdentifierType })
#IsNotEmpty()
identifierType: IdentifierType;
}
ENUM -
export enum IdentifierType {
EMAIL = 'email',
MOBILE = 'mobile',
}
It does work with email but trying to feed a mobile number still does not work.
You have two ways to do this, first with regex:
#Matches(/YOUR_REGEX/, {message: 'identifier should be email or phone'})
identifier: string;
Or you can get the idea from this:
#IsType(Array<(val: any) => boolean>)
#IsType([
val => typeof val == 'string',
val => typeof val == 'boolean',
])
private readonly foo: boolean | string;
Of course it can get more than one validator in one DTO column.
Did you check https://www.npmjs.com/package/class-validator here?
if you want to check mobile number, you can use to #IsMobilePhone(locale: string).

Hyperledger Fabric Node.js Chaincode: Error: can't resolve reference Object from

I am developing a chaincode written in Typescript using fabric-contract-api and the IBM Blockchain Platform plugin for Visual Studio Code. My asset is called Order and, though tests pass perfectly, I am unable to instantiate it. The error I get is the following one:
[6/3/2020 10:51:34] [INFO] fabricvscodelocalfabric-Org1Peer1-06-chaincode-1.0.0|{ [Error: can't resolve reference Object from id Order#]
[6/3/2020 10:51:34] [INFO] fabricvscodelocalfabric-Org1Peer1-06-chaincode-1.0.0| message: 'can\'t resolve reference Object from id Order#',
[6/3/2020 10:51:34] [INFO] fabricvscodelocalfabric-Org1Peer1-06-chaincode-1.0.0| missingRef: 'Object',
[6/3/2020 10:51:34] [INFO] fabricvscodelocalfabric-Org1Peer1-06-chaincode-1.0.0| missingSchema: 'Object' }
I think that the log is not clear enough and I am unable to locate the problem. I understand, as seen in other issues, that the problem is related to fabric-contract-api being unable to handle types like any or Object. However, I am not using that in my code. Everything is declared.
I have started commenting the code function by function to locate the problem and, for example, I get that error if this function is not commented:
#Transaction()
public async createAsset(ctx: Context, assetConfigStringified: string): Promise<string> {
const assetConfig: IAssetConfig = JSON.parse(assetConfigStringified);
const assetId: string = await this.generateInternAssetId(ctx);
const exists = await this.assetExists(ctx, assetId);
if (exists) {
throw new Error(`The asset ${assetId} already exists`);
}
const asset: Asset = new Asset(assetConfig);
const buffer: Buffer = Buffer.from(JSON.stringify(asset));
await ctx.stub.putState(assetId, buffer);
return assetId;
}
These are the the declarations of the interfaces used:
#Object
export interface IComplexType {
propertyA: string;
propertyB: string;
propertyC: string;
propertyD: number;
propertyE?: string;
}
#Object
export interface IAssetConfig {
propertyA: string;
propertyB: IComplexType;
propertyC: IComplexType;
propertyD: string;
propertyE?: string;
}
And this is the asset class:
#Object()
export class Asset {
#Property()
public propertyA: string;
#Property()
public propertyB: IComplexType;
#Property()
public propertyC: IComplexType;
#Property()
public propertyD: string;
#Property()
public propertyE?: string;
constructor(assetConfig: IAssetConfig) {
this.propertyA = assetConfig.propertyA;
this.propertyB = assetConfig.propertyB;
this.propertyC = assetConfig.propertyB;
this.propertyD = assetConfig.propertyD;
if (assetConfig.hasOwnProperty('propertyE')) {
this.propertyE = assetConfig.propertyE;
}
}
}
Thank you very much.

Sequelize-typescript 'HasManyCreateAssociationMixin' is not a function

I have a model in sequelize-typescript, Door.ts:
import { Table, Model, Column, AutoIncrement, PrimaryKey, ForeignKey, DataType, AllowNull, BelongsTo, HasMany } from 'sequelize-typescript';
import { Location } from '#modules/location';
import { AkilesServiceV1, AkilesServiceV0, IDoorService } from '#services/DoorService';
import { BelongsToGetAssociationMixin } from 'sequelize/types';
import { DoorLog } from '#modules/door_log';
import { HasManyCreateAssociationMixin } from 'sequelize';
#Table({ tableName: 'door' })
class Door extends Model<Door> {
#PrimaryKey
#AutoIncrement
#Column
id!: number;
#AllowNull(false)
#Column
type!: string;
#Column
button_id!: string;
#Column
gadget_id!: string;
#Column
action_id!: string;
#AllowNull(false)
#Column(DataType.ENUM('vehicular','pedestrian'))
access_type!: 'vehicular' | 'pedestrian';
#AllowNull(false)
#Column
description_tag!: string;
#Column(DataType.VIRTUAL)
description!: string;
#ForeignKey(() => Location)
#AllowNull(false)
#Column
location_id!: number;
#BelongsTo(() => Location)
location!: Location;
#HasMany(() => DoorLog)
door_logs!: DoorLog[];
public getLocation!: BelongsToGetAssociationMixin<Location>;
public createDoorLog!: HasManyCreateAssociationMixin<DoorLog>;
public async open () {
let doorService: IDoorService;
switch(this.type) {
case 'akiles-v0':
doorService = new AkilesServiceV0();
break;
case 'akiles-v1':
doorService = new AkilesServiceV1();
break;
default:
doorService = new AkilesServiceV1();
break;
}
//await doorService.open(this);
return await this.createDoorLog({ door_id: this.id, timestamp: new Date() });
}
public async getParking() {
const location: Location = await this.getLocation();
return await location.getParking();
}
}
export default Door
As you can see it has these two functions associated with Mixins:
public getLocation!: BelongsToGetAssociationMixin<Location>;
public createDoorLog!: HasManyCreateAssociationMixin<DoorLog>;
The first works perfectly using it like this: await this.getLocation(). However, the second when I call it like this: await this.createDoorlog ({door_id: this.id, timestamp: new Date ()}) returns the following error:
TypeError: this.createDoorLog is not a function
I've also tried calling the function without parameters but got the same result. I don't understand why the two functions, while created almost identically, behave differently. Am I missing something with HasManyCreateAssociationMixin?
Thank you.
For when I inevitably come across this question again perplexed by the same problem. The answer Is to ad "as" to the #HasMany mixin. Sequelize appears to have issues with camelcase classes.
So in this case adding
#HasMany(() => DoorLog, options: {as: "doorLog" })
door_logs!: DoorLog[];
or something along these lines should allow you to use this mixin

NestJS - Creating Business Objects From Other Services

Nowadays I'm playing around with NestJS and trying to understand the best practices of NestJS world.
By following official documentations I have created service/dto/entity/controller for my business object called 'Cat'.
(also using SequelizeJS)
cat.entity.ts
#Table({
freezeTableName: true,
})
export class Cat extends Model<Cat> {
#AllowNull(false)
#Column
name: string;
#Column
breed: string;
}
create-cat.dto.ts
export class CreateCatDto {
#IsString()
readonly name: string;
#IsString()
readonly breed: string;
}
cat.service.ts
export class CatService {
constructor(#Inject('CAT_REPOSITORY') private readonly CAT_REPOSITORY: typeof Cat) {}
async create(createCatDto: CreateCatDto): Promise<Cat> {
const cat = new Cat();
cat.name = createCatDto.name;
cat.breed = createCatDto.breed;
return await cat.save();
}
}
Now I can make POST requests to my controller and create Cat objects successfully. But I want to create a 'Cat' from other service and the create() method only takes CreateCatDto which I cannot/shouldn't initialize (it's read-only).
How can I call create(createCatDto: CreateCatDto) from other services? How you are handling this kind of requirements with NestJS? Should I create one more method like createFromEntity(cat: Cat) and use it?
try this:
const cat: CreateCatDto = { name: 'Miau', breed: 'some' };

Resources