Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Ever wrote crap code to deal with parsing command-line options? No more. Here's how it should look like (in CoffeeScript):
options = require('dreamopt') [
"Usage: myscript [options] <source> [<destination>]"
" <source> Source file to compile into css #required"
" <destination> Destination file (defaults to source file with .css extension)", (value, options) ->
if !value
return options.source.replace(/\.mess/, '') + '.css'
"Processing options:"
" -n, --dry-run Don't write anything to disk"
" -m, --mode MODE Set execution mode: easy, medium, hard (defaults to medium)"
"Connection options:"
" -p, --port PORT Port to connect to (default: 80)"
" -h, --host HOST Host to connect to (default is localhost)"
"Getting useful information:"
" --reporters Print a list of reporters and exit", ->
console.log "Reporters: foo, bar, boz"
process.exit 0
]
console.log JSON.stringify(options, null, 2)
Try to run it as node examples/foo.js
:
Error: Missing required argument #1: source
Now run it as node examples/foo.js myfile.mess
:
{
"mode": "medium",
"port": 80,
"host": "localhost",
"source": "myfile.mess",
"destination": "myfile.css",
"argv": [
"myfile.mess",
"myfile.css"
]
}
npm install dreamopt
Overview:
--help
Option syntax details:
--long
), short options with a single dash (-s
)-
) is considered a positional argument rather than an option (and usually signifies reading from stdin)--
ends option processing-sVAL
, -s VAL
, --long=VAL
or --long VAL
-xc
)-xcfMYFILE
, -pi.tmp
)This module can called with up to three arguments:
options = require('dreamopt')(spec, [options])
where:
spec
is a required array of stringsoptions
is an optional hash (i.e. a JavaScript object)The following options can be specified:
options.argv
is an array of command-line arguments, defaults to process.argv.slice(2)
options.customTags
is a hash with custom tag handlersoptions.error(err)
is a function that handles syntax error, the default one prints err.message
and exitsoptions.help(usage)
is a function that handles --help
, the default one prints usage
and exitsoptions.loadCommandSyntax(command)
is a function that returns the subcommand syntax for the given commandEach line of spec
can be:
Usage: blah blag
— a banner, it is displayed at the very top of the usage infoSomething:
— a header, it is displayed verbatim with appropriate spacing; if you don't define any headers, dreamopt will add the default ones as needed (“Arguments:” and “Options:”)-s, --long <value> Description #tag1 #tag2(val2)
— option definition; must start with at least one space; if description or tags are specified, they must be separated from the option itself by at least two spaces; tags must be in the end and may have optional values-s, --long VALUE Description #tag1 #tag2(val2)
— can use VALUE
instead of <value>
<arg> Description #tag1 #tag2
— positional argument definition, same format as optionsARG Description #tag1 #tag2
— can use ARG
instead of <arg>
command Description
followed by a handler function (optional) and an array (required unless you provide options.loadCommandSyntax
) — subcommand definitionAny other lines that don't start with whitespace are output verbatim, as a paragraph of text. (Lines that start with whitespace must conform to option, argument or subcommand syntax.)
Syntax:
options = require('dreamopt') [
"Commands:"
" init Create a new repository in the current folder", []
" commit Commit the staged changes", []
"Common options:"
" -v, --verbose Print tons of useless info"
]
switch options.command
when 'init'
...
when 'commit'
...
You can specify a function to run for each command:
doInit = (options) ->
...
doCommit = (options) ->
...
options = require('dreamopt') [
"Commands:"
" init Create a new repository in the current folder", [], doInit
" commit Commit the staged changes", [], doCommit
]
Command-specific options and help:
INIT_SYNTAX = [
"Create a new repository in the current folder."
" -b, --bare Create a bare repository"
]
COMMIT_SYNTAX = [
"Commit the staged changes."
"Usage: git commit [options] [<file>...]"
" -a, --all Auto-add all changes"
"<file> The file to commit #list"
]
options = require('dreamopt') [
"Commands:"
" init", doInit, INIT_SYNTAX
" commit", doCommit, COMMIT_SYNTAX
"Common options:"
" -v, --verbose Print tons of useless info"
]
Modularizing your code:
# main.coffee:
options = require('dreamopt') [
"Commands:
" init"
" commit"
"Common options:"
" -v, --verbose Print tons of useless info"
], {
loadCommandSyntax: (command) -> require("./commands/#{command}").usage
}
require("./commands/#{options.command}").run(options)
# commands/commit.coffee:
exports.usage = [
"Commit the staged changes."
"Usage: git commit [options] [<file>...]"
" -a, --all Auto-add all changes"
"<file> The file to commit #list"
]
exports.run = (options) ->
...
Commands can be nested, which results in options.command
, options.subcommand
, options.subsubcommand
etc; loadCommandSyntax is called with a space-separated command name for nested commands.
Argument values are automatically coerced to numbers if possible, otherwise they are provided as strings. You can specify one of the following tags to change coercion rules:
#string
disables coercion and always returns a string#int
always coerces to int, giving an error if that's impossibleYou can define custom tags to handle coercion, validation or any other processing. For example, to parse a simple YYYY-MM-DD date format, you can do:
options = require('../lib/dreamopt') [
"-f, --from DATE Only process records from the given date #date"
], {
date: (value, options, optionName) ->
if isNaN(new Date(value))
throw new Error("Invalid date for option #{optionName}")
new Date(value)
}
console.log "Year: " + options.from?.getFullYear()
Tag functions are invoked with four arguments (value, options, optionName, tagValue)
:
value
is the value of the current optionoptions
is the options hash built so faroptionName
is useful when referring to the current option in an error messagetagValue
is the value of the tag if any; for example, for #date(today)
the tagValue would be 'today'
#required
marks a required option or argument#var(fieldName)
overrides the options field for this option (i.e. the value is stored into options.fieldName
)#default(value)
specifies a default value#list
marks an option that may be used multiple times; the final value is a JavaScript array#fancydefault
forces the callback function associated with the current option to be called even when an argument is not provided and no default is set; in this case, the original value will be null
and your function is expected to return a better one#delayfunc
delays invocation of the callback function until all other options and arguments are processed; this is useful for options like --help
or --print-reporters
, when you want all normal options to be handled and validated before the callback is invoked; the return value of such callback functions is ignoredAdditionally, you may encounter the following internal tags in the source code:
#flag
denotes a no-values option (which is always treated as boolean)#acceptsno
is set for options which use --[no-]something
in their definition; all boolean option accept --no-option variant to turn them off, but only options explicitly specified as such are documented as accepting --no variants in usage infoIf you don't define a --help
option, it is provided for you automatically and prints a usage info like this:
Usage: myscript [options] <source.mess> [<destination.css>]
Arguments:
<source> Source file to compile into css
<destination> Destination file (defaults to source file with .css extension)
Processing options:
-n, --dry-run Don't write anything to disk
-m, --mode MODE Set execution mode: easy, medium, hard (defaults to medium)
Connection options:
-p, --port PORT Port to connect to (default: 80)
-h, --host HOST Host to connect to (default is localhost)
Getting useful information:
--reporters Print a list of reporters and exit
-h, --help Display this usage information
You can provide options.help(usageText)
function to customize the way this usage info is printed; the default implementation outputs the argument via process.stdout.write
and executes process.exit(0)
.
FAQs
Command-line parser with readable syntax from your sweetest dreams
The npm package dreamopt receives a total of 254,043 weekly downloads. As such, dreamopt popularity was classified as popular.
We found that dreamopt 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.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.