Research
Security News
Kill Switch Hidden in npm Packages Typosquatting Chalk and Chokidar
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
⚠️this library is totally unstable and incomplete.
This library aims to make it easy to read, persist, and prompt for required settings for CLI's.
Two secondary goals we might not reach but want to consider:
Here are some sources of CLI config:
package.json
, an rc
file read by cosmiconfig, or some other special file e.g. toml
)While offline, we may also lean on caches as sources of state:
Config vs State
We make a (possibly confusing?) distinction between config vs state. Config is static - it is what it is before the CLI starts, and doesn't change. State is dynamic, you can set state during your CLI session and expect it to persist between sessions. If a state is missing, we can prompt for it, and then offer to persist it, or tell the user how to override it in future with a flag.
For maximum context, these examples will show how to integrate cli-state
with oclif.
We start with a custom base command:
// src/base.ts
import Command from '@oclif/command';
import { initCLIState } from 'cli-state';
export default abstract class extends Command {
async init() {
// do some initialization
initCLIState({ projectStatePath: '.exampleCLI' });
}
}
this makes sure we initialize our CLI state with the basic settings we want every time we run a command.
Then we are free to read and write to and from our configs wherever we want:
// src/commands/hello.ts
import { flags } from '@oclif/command';
import BaseCommand from '../../base';
import { globalState, projectState } from 'cli-state';
export default class Hello extends BaseCommand {
static description = 'describe the command here';
static examples = [
`$ example hello
hello world from ./src/hello.ts!
`,
];
static flags = {
help: flags.help({ char: 'h' }),
// flag with a value (-n, --name=VALUE)
name: flags.string({ char: 'n', description: 'name to print' }),
file: flags.string({ description: 'file for writing' }),
// flag with no value (-f, --force)
force: flags.boolean({ char: 'f' }),
};
static args = [{ name: 'file' }];
async run() {
const { args, flags } = this.parse(Hello);
const name = flags.name || 'world';
this.log(`hello ${name} from ./src/commands/hello.ts`);
globalState.set('name', name);
const type = globalState.get('name');
console.log(type, typeof type); // types are preserved after deserialization
console.log('globalState path:', globalState.path); // where the globalState is stored
projectState.set('name'); // same thing for project config
}
}
cli-state
is really a thin wrapper over a few libraries. Here is the public API (there are no default exports):
initCLIState
initCLIState({
projectStatePath: string;
globalConfOptions?: Conf.Options<ConfigTypes>;
projectConfOptions?: Conf.Options<ConfigTypes>;
frecencyOpts?: {
idAttribute?: string | idAttrFn;
timeStampsLimit?: number;
recentSelectionsLimit?: number;
exactQueryMatchWeight?: number;
subQueryMatchWeight?: number;
recentSelectionsMatchWeight?: number;
};
})
This must be run before any of the other functions.
Only projectStatePath
is mandatory, and should reflect where you want your project config stored, e.g. in a .netlify
folder.
Both globalConfOptions
and projectConfOptions
take the same options as https://github.com/sindresorhus/conf, which you can see here. In particular you may wish to define a schema. We tweak the options slightly: globalConfOptions
sets a telemetry
flag and a unique cliId
by default, and projectConfOptions
overrides conf's cwd
to make the file project specific.
frecencyOpts
take from https://github.com/mixmaxhq/frecency except key
and storageProvider
have been omitted. You set key
later and storageProvider
will be set for you. By default, we have made the idAttribute
be "value"
instead of "_id"
, since that is far more common for CLI prompting. but you can override this.
globalState
and projectState
These two objects have the entire public instance API of https://github.com/sindresorhus/conf. You can view them here: https://github.com/sindresorhus/conf#instance, in particular .get
, .set
, and .store
.
These are singletons for the life of your CLI.
accessGlobalFrecency
accessGlobalFrecency: (key: string) => Frecency;
This function takes a key and creates a Frecency object, which is stored in the user's default/specified cache folder according to env-paths
. This is explicitly meant for recording and sorting prompt inputs, for example Autocompletes, to help learn from usage.
You can create as many as you like, as long as your keys are unique. Remember we overrode the default idAttribute
to value
instead of _id
, but if you want to override it further, pass frecencyOpts.idAttribute
during initialization.
The frecency docs aren't great so here is a working example.
const freq = accessGlobalFrecency('country');
const choices = freq.sort({
results: ['apple', 'banana', 'pear', 'orange', 'pineapple'].map(normalize),
// keepScores: true,
});
const question = {
type: 'autocomplete',
name: 'fruits',
message: 'Where to?',
limit: 5,
suggest(input: string, choices: Choice[]) {
const list = choices.filter(caseInsensitiveFilter(input));
return freq.sort({ searchQuery: input, results: list });
},
choices,
};
let { fruits } = await prompt(question);
const tosave = { searchQuery: fruits, selectedId: fruits };
freq.save(tosave);
console.log({ fruits });
// helper functions defined
type Choice = { value: string; message: string; name: string };
function normalize(foo: string) {
return { value: foo, message: foo, name: foo };
}
function caseInsensitiveFilter(input: string) {
return (choice: Choice) =>
choice.message.toLowerCase().startsWith(input.toLowerCase());
}
This project was bootstrapped with TSDX.
FAQs
> ⚠️this library is totally unstable and incomplete.
The npm package cli-state receives a total of 9 weekly downloads. As such, cli-state popularity was classified as not popular.
We found that cli-state demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
Security News
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.