New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@eik/sink-file-system

Package Overview
Dependencies
Maintainers
0
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@eik/sink-file-system - npm Package Compare versions

Comparing version 1.0.1 to 1.0.2

7

CHANGELOG.md

@@ -0,1 +1,8 @@

## [1.0.2](https://github.com/eik-lib/sink-file-system/compare/v1.0.1...v1.0.2) (2024-11-13)
### Bug Fixes
* **deps:** update dependency @metrics/client to v2.5.4 ([#16](https://github.com/eik-lib/sink-file-system/issues/16)) ([800b226](https://github.com/eik-lib/sink-file-system/commit/800b22602af2d7e326948d8914262ee0226356b2))
## [1.0.1](https://github.com/eik-lib/sink-file-system/compare/v1.0.0...v1.0.1) (2024-07-29)

@@ -2,0 +9,0 @@

536

lib/main.js

@@ -1,9 +0,9 @@

import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { ReadFile } from '@eik/common';
import Sink from '@eik/sink';
import Metrics from '@metrics/client';
import mime from 'mime';
import { rimraf } from 'rimraf';
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { ReadFile } from "@eik/common";
import Sink from "@eik/sink";
import Metrics from "@metrics/client";
import mime from "mime";
import { rimraf } from "rimraf";

@@ -15,5 +15,5 @@ /**

const etagFromFsStat = (stat) => {
const mtime = stat.mtime.getTime().toString(16);
const size = stat.size.toString(16);
return `W/"${size}-${mtime}"`;
const mtime = stat.mtime.getTime().toString(16);
const size = stat.size.toString(16);
return `W/"${size}-${mtime}"`;
};

@@ -42,298 +42,298 @@

export default class SinkFileSystem extends Sink {
/**
* @type {Required<SinkFileSystemOptions>}
*/
_config;
/**
* @type {Required<SinkFileSystemOptions>}
*/
_config;
/** @type {import('@metrics/client')} */
_metrics;
/** @type {import('@metrics/client')} */
_metrics;
/**
* @param {SinkFileSystemOptions} options
*/
constructor(options = {}) {
super();
this._config = {
sinkFsRootPath: path.join(os.tmpdir(), '/eik-files'),
...options,
};
this._metrics = new Metrics();
this._counter = this._metrics.counter({
name: 'eik_core_sink_fs',
description:
'Counter measuring access to the file system storage sink',
labels: {
operation: 'n/a',
success: false,
access: false,
},
});
}
/**
* @param {SinkFileSystemOptions} options
*/
constructor(options = {}) {
super();
this._config = {
sinkFsRootPath: path.join(os.tmpdir(), "/eik-files"),
...options,
};
this._metrics = new Metrics();
this._counter = this._metrics.counter({
name: "eik_core_sink_fs",
description:
"Counter measuring access to the file system storage sink",
labels: {
operation: "n/a",
success: false,
access: false,
},
});
}
get metrics() {
return this._metrics;
}
get metrics() {
return this._metrics;
}
/**
* @param {string} filePath
* @param {string} contentType
* @returns {Promise<import('node:stream').Writable>}
*/
write(filePath, contentType) {
return new Promise((resolve, reject) => {
const operation = 'write';
/**
* @param {string} filePath
* @param {string} contentType
* @returns {Promise<import('node:stream').Writable>}
*/
write(filePath, contentType) {
return new Promise((resolve, reject) => {
const operation = "write";
try {
Sink.validateFilePath(filePath);
Sink.validateContentType(contentType);
} catch (error) {
this._counter.inc({ labels: { operation } });
reject(error);
return;
}
try {
Sink.validateFilePath(filePath);
Sink.validateContentType(contentType);
} catch (error) {
this._counter.inc({ labels: { operation } });
reject(error);
return;
}
const pathname = path.join(this._config.sinkFsRootPath, filePath);
const pathname = path.join(this._config.sinkFsRootPath, filePath);
if (pathname.indexOf(this._config.sinkFsRootPath) !== 0) {
this._counter.inc({ labels: { operation } });
reject(new Error(`Directory traversal - ${filePath}`));
return;
}
if (pathname.indexOf(this._config.sinkFsRootPath) !== 0) {
this._counter.inc({ labels: { operation } });
reject(new Error(`Directory traversal - ${filePath}`));
return;
}
const dir = path.dirname(pathname);
const dir = path.dirname(pathname);
fs.mkdir(
dir,
{
recursive: true,
},
(error) => {
if (error) {
this._counter.inc({
labels: { access: true, operation },
});
reject(
new Error(`Could not create directory - ${dir}`),
);
return;
}
fs.mkdir(
dir,
{
recursive: true,
},
(error) => {
if (error) {
this._counter.inc({
labels: { access: true, operation },
});
reject(
new Error(`Could not create directory - ${dir}`),
);
return;
}
const stream = fs.createWriteStream(pathname, {
autoClose: true,
emitClose: true,
});
const stream = fs.createWriteStream(pathname, {
autoClose: true,
emitClose: true,
});
this._counter.inc({
labels: {
success: true,
access: true,
operation,
},
});
this._counter.inc({
labels: {
success: true,
access: true,
operation,
},
});
resolve(stream);
},
);
});
}
resolve(stream);
},
);
});
}
/**
* @param {string} filePath
* @throws {Error} if the file does not exist
* @returns {Promise<import('@eik/common').ReadFile>}
*/
read(filePath) {
return new Promise((resolve, reject) => {
const operation = 'read';
/**
* @param {string} filePath
* @throws {Error} if the file does not exist
* @returns {Promise<import('@eik/common').ReadFile>}
*/
read(filePath) {
return new Promise((resolve, reject) => {
const operation = "read";
try {
Sink.validateFilePath(filePath);
} catch (error) {
this._counter.inc({ labels: { operation } });
reject(error);
return;
}
try {
Sink.validateFilePath(filePath);
} catch (error) {
this._counter.inc({ labels: { operation } });
reject(error);
return;
}
const pathname = path.join(this._config.sinkFsRootPath, filePath);
const pathname = path.join(this._config.sinkFsRootPath, filePath);
if (pathname.indexOf(this._config.sinkFsRootPath) !== 0) {
this._counter.inc({ labels: { operation } });
reject(new Error(`Directory traversal - ${filePath}`));
return;
}
if (pathname.indexOf(this._config.sinkFsRootPath) !== 0) {
this._counter.inc({ labels: { operation } });
reject(new Error(`Directory traversal - ${filePath}`));
return;
}
const closeFd = (fd) => {
fs.close(fd, (error) => {
if (error) {
this._counter.inc({
labels: {
access: true,
operation,
},
});
return;
}
this._counter.inc({
labels: {
success: true,
access: true,
operation,
},
});
});
};
const closeFd = (fd) => {
fs.close(fd, (error) => {
if (error) {
this._counter.inc({
labels: {
access: true,
operation,
},
});
return;
}
this._counter.inc({
labels: {
success: true,
access: true,
operation,
},
});
});
};
fs.open(pathname, 'r', (error, fd) => {
if (error) {
this._counter.inc({
labels: {
access: true,
operation,
},
});
reject(error);
return;
}
fs.open(pathname, "r", (error, fd) => {
if (error) {
this._counter.inc({
labels: {
access: true,
operation,
},
});
reject(error);
return;
}
fs.fstat(fd, (err, stat) => {
if (err) {
closeFd(fd);
reject(err);
return;
}
fs.fstat(fd, (err, stat) => {
if (err) {
closeFd(fd);
reject(err);
return;
}
if (!stat.isFile()) {
closeFd(fd);
reject(new Error(`Not a file - ${pathname}`));
return;
}
if (!stat.isFile()) {
closeFd(fd);
reject(new Error(`Not a file - ${pathname}`));
return;
}
const mimeType =
mime.getType(pathname) || 'application/octet-stream';
const etag = etagFromFsStat(stat);
const mimeType =
mime.getType(pathname) || "application/octet-stream";
const etag = etagFromFsStat(stat);
const obj = new ReadFile({
mimeType,
etag,
});
const obj = new ReadFile({
mimeType,
etag,
});
obj.stream = fs.createReadStream(pathname, {
autoClose: true,
fd,
});
obj.stream = fs.createReadStream(pathname, {
autoClose: true,
fd,
});
obj.stream.on('error', () => {
this._counter.inc({
labels: {
access: true,
operation,
},
});
});
obj.stream.on("error", () => {
this._counter.inc({
labels: {
access: true,
operation,
},
});
});
obj.stream.on('end', () => {
this._counter.inc({
labels: {
success: true,
access: true,
operation,
},
});
});
obj.stream.on("end", () => {
this._counter.inc({
labels: {
success: true,
access: true,
operation,
},
});
});
resolve(obj);
});
});
});
}
resolve(obj);
});
});
});
}
/**
* @param {string} filePath
* @returns {Promise<void>}
*/
delete(filePath) {
return new Promise((resolve, reject) => {
const operation = 'delete';
/**
* @param {string} filePath
* @returns {Promise<void>}
*/
delete(filePath) {
return new Promise((resolve, reject) => {
const operation = "delete";
try {
Sink.validateFilePath(filePath);
} catch (error) {
this._counter.inc({ labels: { operation } });
reject(error);
return;
}
try {
Sink.validateFilePath(filePath);
} catch (error) {
this._counter.inc({ labels: { operation } });
reject(error);
return;
}
const pathname = path.join(this._config.sinkFsRootPath, filePath);
const pathname = path.join(this._config.sinkFsRootPath, filePath);
if (pathname.indexOf(this._config.sinkFsRootPath) !== 0) {
this._counter.inc({ labels: { operation } });
reject(new Error(`Directory traversal - ${filePath}`));
return;
}
if (pathname.indexOf(this._config.sinkFsRootPath) !== 0) {
this._counter.inc({ labels: { operation } });
reject(new Error(`Directory traversal - ${filePath}`));
return;
}
rimraf(pathname)
.then(() => {
this._counter.inc({
labels: {
success: true,
access: true,
operation,
},
});
resolve();
})
.catch((error) => {
this._counter.inc({ labels: { access: true, operation } });
reject(error);
});
});
}
rimraf(pathname)
.then(() => {
this._counter.inc({
labels: {
success: true,
access: true,
operation,
},
});
resolve();
})
.catch((error) => {
this._counter.inc({ labels: { access: true, operation } });
reject(error);
});
});
}
/**
* @param {string} filePath
* @throws {Error} if the file does not exist
* @returns {Promise<void>}
*/
exist(filePath) {
return new Promise((resolve, reject) => {
const operation = 'exist';
/**
* @param {string} filePath
* @throws {Error} if the file does not exist
* @returns {Promise<void>}
*/
exist(filePath) {
return new Promise((resolve, reject) => {
const operation = "exist";
try {
Sink.validateFilePath(filePath);
} catch (error) {
this._counter.inc({ labels: { operation } });
reject(error);
return;
}
try {
Sink.validateFilePath(filePath);
} catch (error) {
this._counter.inc({ labels: { operation } });
reject(error);
return;
}
const pathname = path.join(this._config.sinkFsRootPath, filePath);
const pathname = path.join(this._config.sinkFsRootPath, filePath);
if (pathname.indexOf(this._config.sinkFsRootPath) !== 0) {
this._counter.inc({ labels: { operation } });
reject(new Error(`Directory traversal - ${filePath}`));
return;
}
if (pathname.indexOf(this._config.sinkFsRootPath) !== 0) {
this._counter.inc({ labels: { operation } });
reject(new Error(`Directory traversal - ${filePath}`));
return;
}
fs.stat(pathname, (error, stat) => {
this._counter.inc({
labels: { success: true, access: true, operation },
});
fs.stat(pathname, (error, stat) => {
this._counter.inc({
labels: { success: true, access: true, operation },
});
if (stat && stat.isFile()) {
resolve();
return;
}
if (stat && stat.isFile()) {
resolve();
return;
}
if (error) {
reject(error);
return;
}
reject();
});
});
}
if (error) {
reject(error);
return;
}
reject();
});
});
}
get [Symbol.toStringTag]() {
return 'SinkFileSystem';
}
get [Symbol.toStringTag]() {
return "SinkFileSystem";
}
}
{
"name": "@eik/sink-file-system",
"version": "1.0.1",
"version": "1.0.2",
"description": "Sink implementation that persists files on the local file system.",

@@ -15,8 +15,9 @@ "main": "lib/main.js",

"scripts": {
"clean": "rimraf .tap node_modules types",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"test": "run-s test:*",
"test:unit": "tap --disable-coverage --allow-empty-coverage tests/**/*.js",
"test:types": "tsc --project tsconfig.test.json",
"types": "tsc --declaration --emitDeclarationOnly"
"test": "tap --disable-coverage --allow-empty-coverage tests/**/*.js",
"types": "run-s types:module types:test",
"types:module": "tsc",
"types:test": "tsc --project tsconfig.test.json"
},

@@ -39,20 +40,18 @@ "repository": {

"@eik/sink": "1.2.5",
"@metrics/client": "2.5.3",
"mime": "3.0.0",
"rimraf": "5.0.8"
"@metrics/client": "2.5.4",
"mime": "3.0.0"
},
"devDependencies": {
"@semantic-release/changelog": "6.0.3",
"@semantic-release/git": "10.0.1",
"@eik/eslint-config": "1.0.4",
"@eik/prettier-config": "1.0.1",
"@eik/semantic-release-config": "1.0.0",
"@types/mime": "3.0.4",
"@types/readable-stream": "4.0.15",
"eslint": "9.1.1",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.1.3",
"globals": "15.0.0",
"npm-run-all": "4.1.5",
"prettier": "3.3.2",
"semantic-release": "24.0.0",
"@types/readable-stream": "4.0.18",
"eslint": "9.11.1",
"npm-run-all2": "5.0.2",
"prettier": "3.3.3",
"rimraf": "6.0.1",
"semantic-release": "24.1.3",
"tap": "18.8.0"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc