Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

dockerfilelint

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dockerfilelint - npm Package Compare versions

Comparing version 0.0.8 to 0.0.9

.eslintrc

21

lib/index.js

@@ -122,6 +122,7 @@ 'use strict';

case 'run':
var subcommands = [];
run_checks.aptget_commands(args).forEach(function(aptget_command) {
var aptgetSubcommands = [];
var commands = run_checks.split_commands(args);
run_checks.aptget_commands(commands).forEach(function(aptget_command, index) {
var subcommand = run_checks.aptget_subcommand(aptget_command);
subcommands.push(subcommand);
aptgetSubcommands.push(subcommand);
if (["install", "remove", "upgrade"].indexOf(subcommand) > -1) {

@@ -138,3 +139,3 @@ if (!run_checks.aptget_hasyes(aptget_command)) {

} else if (subcommand === 'update') {
if (!run_checks.includes_rmaptlists(args)) {
if (!run_checks.follows_rmaptlists(commands, index)) {
items.push(messages.build('apt-get_missing_rm', line));

@@ -148,5 +149,15 @@ }

});
if ((subcommands.indexOf('update') > -1) && (subcommands.indexOf('install') === -1)) {
if (aptgetSubcommands.indexOf('update') > -1 && aptgetSubcommands.indexOf('install') === -1) {
items.push(messages.build('apt-get-update_require_install', line));
}
run_checks.apk_commands(commands).forEach(function(apk_command, index) {
var subcommand = run_checks.apk_subcommand(apk_command);
if (subcommand === 'add') {
if (!run_checks.apkadd_hasnocache(apk_command)) {
if (!run_checks.apkadd_hasupdate(apk_command) || !run_checks.follows_rmapkcache(commands, index)) {
items.push(messages.build('apkadd-missing_nocache_or_updaterm', line));
}
}
}
});
break;

@@ -153,0 +164,0 @@ case 'cmd':

@@ -84,2 +84,8 @@ var reference = module.exports = {

},
'apkadd-missing_nocache_or_updaterm': {
'title': 'Consider `--no-cache or --update with rm -rf /var/cache/apk/*`',
'description': `Consider using a \`--no-cache\` (supported in alpine linux >= 3.3) or \`--update\` followed by the command \`rm -rf /var/cache/apk/*\` when \`apk\` adding packages. This will result in a smaller image size. For
more information, see [this link](https://github.com/gliderlabs/docker-alpine/blob/master/docs/usage.md)`,
'category': 'Optimization'
},
'invalid_port': {

@@ -86,0 +92,0 @@ 'title': 'Invalid Port Exposed',

var commands = module.exports = {
// Given a bash command (command && command && command, etc), return an array
// of all apt-get full commands found
aptget_commands: function(args) {
var aptgetCommands = [];
var commands = args.split('&&');
// return an array of all `prefix` commands found
var filter_commands = function(commands, prefix) {
var prefixCommands = [];
if (commands) {
commands.forEach(function(command) {
command = command.trim();
// Treat apt-get as a special command and split it apart
if (command.startsWith('apt-get')) {
aptgetCommands.push(command);
if (command.startsWith(prefix)) {
prefixCommands.push(command);
}
});
}
return prefixCommands;
};
return aptgetCommands;
var get_subcommand = function(command) {
var subcommand = '';
command.match(/\S+/g).slice(1).some(function(part) {
if (!part.startsWith('-')) {
subcommand = part;
return true;
}
});
return subcommand;
};
var command_has_part = function(command, commandPart) {
return command.match(/\S+/g).some(function(part) {
return part.trim() === commandPart;
});
};
var command_has_flag = function(command, commandFlag) {
if (commandFlag.startsWith('--')) {
return command.match(/\S+/g).some(function(part) {
return part.trim() === commandFlag;
});
} else if (commandFlag.startsWith('-')) {
return command.match(/\S+/g).some(function(part) {
if (part.startsWith('--') || !part.startsWith('-')) {
return false;
}
return part.trim().substr(1).split('').some(function(char) {
return char === commandFlag.substr(1);
});
});
}
throw 'bad flag';
};
var get_nextcommands = function(commands, index) {
return commands.slice(index+1);
};
var commands = module.exports = {
// Given a bash command (command && command && command, etc), return an array
// of all commands
split_commands: function(args) {
return args.split('&&').map(function(command) {
return command.trim();
});
},
aptget_commands: function(commands) {
return filter_commands(commands, 'apt-get');
},
// return the subcommand from apt-get
aptget_subcommand: function(aptgetCommand) {
var commandParts = aptgetCommand.match(/\S+/g);
if (commandParts.length === 1) {
return "";
}
// Is the subcommand always immediately after apt-get?
return commandParts[1];
return get_subcommand(aptgetCommand);
},

@@ -33,15 +75,5 @@

aptget_hasyes: function(aptgetCommand) {
var commandParts = aptgetCommand.match(/\S+/g);
if (commandParts.length === 1) {
return "";
}
var found = false;
commandParts.forEach(function(commandPart) {
if (commandPart.trim().toLowerCase() === "-y") {
found = true;
}
});
return found;
return command_has_flag(aptgetCommand, '-y')
|| command_has_flag(aptgetCommand, '--yes')
|| command_has_flag(aptgetCommand, '--assume-yes');
},

@@ -51,41 +83,67 @@

aptget_hasnorecommends: function(aptgetCommand) {
var commandParts = aptgetCommand.match(/\S+/g);
if (commandParts.length === 1) {
return "";
}
return command_has_flag(aptgetCommand, '--no-install-recommends');
},
var found = false;
commandParts.forEach(function(commandPart) {
if (commandPart.trim().toLowerCase() === "--no-install-recommends") {
found = true;
// check that command includes a rm -rf /var/lib/apt/lists/*
follows_rmaptlists: function(commands, index) {
return get_nextcommands(commands, index).some(function(command) {
if (command.split(' ')[0] !== 'rm') {
return false;
}
if (!command_has_part(command, '/var/lib/apt/lists/*')) {
return false;
}
if (!command_has_flag(command, '-r')
&& !command_has_flag(command, '-R')
&& !command_has_flag(command, '--recursive')) {
return false;
}
if (!command_has_flag(command, '-f')
&& !command_has_flag(command, '--force')) {
return false;
}
return true;
});
},
return found;
apk_commands: function(commands) {
return filter_commands(commands, 'apk');
},
// check that command includes a rm -rf /var/lib/apt/lists/*
includes_rmaptlists: function(args) {
var isAllFound = false;
var commands = args.split('&&');
commands.forEach(function(command) {
command = command.trim();
if (command.startsWith('rm')) {
var commandParts = command.match(/\S+/g);
var isCorrectPath = false,
isCorrectFlags = false;
commandParts.forEach(function(commandPart) {
if (commandPart.includes('-') && commandPart.includes('r') && commandPart.includes('f')) {
isCorrectFlags = true;
} else if (commandPart === '/var/lib/apt/lists/*') {
isCorrectPath = true;
}
});
// return the subcommand from apk
apk_subcommand: function(apkCommand) {
return get_subcommand(apkCommand);
},
isAllFound = isAllFound || (isCorrectFlags && isCorrectPath);
// check that an apk add command has --no-cache flag
apkadd_hasnocache: function(apkaddCommand) {
return command_has_flag(apkaddCommand, '--no-cache');
},
// check that an apk add command has --update flag
apkadd_hasupdate: function(apkaddCommand) {
return command_has_flag(apkaddCommand, '--update');
},
// check that command includes a rm -rf /var/cache/apk/*
follows_rmapkcache: function(commands, index) {
return get_nextcommands(commands, index).some(function(command) {
if (command.split(' ')[0] !== 'rm') {
return false;
}
if (!command_has_part(command, '/var/cache/apk/*')) {
return false;
}
if (!command_has_flag(command, '-r')
&& !command_has_flag(command, '-R')
&& !command_has_flag(command, '--recursive')) {
return false;
}
if (!command_has_flag(command, '-f')
&& !command_has_flag(command, '--force')) {
return false;
}
return true;
});
return isAllFound;
}
}
};
{
"name": "dockerfilelint",
"version": "0.0.8",
"version": "0.0.9",
"description": "A linter for Dockerfiles to find bugs and encourage best practices",
"main": "./lib/index.js",
"scripts": {
"test": "mocha"
"test": "nyc mocha",
"coverage": "nyc report --reporter=text-lcov | coveralls"
},

@@ -25,6 +26,13 @@ "repository": {

"dependencies": {
"lodash": "^4.3.0",
"yargs": "^3.32.0"
},
"devDependencies": {
"chai": "^3.5.0",
"coveralls": "^2.11.6",
"eslint": "^1.10.3",
"eslint-config-defaults": "^9.0.0",
"mocha": "^2.4.5",
"yargs": "^3.32.0"
"nyc": "^5.6.0"
}
}
# Linter and validator for Dockerfile
[![Coverage Status](https://coveralls.io/repos/github/replicatedhq/dockerfilelint/badge.svg?branch=master)](https://coveralls.io/github/replicatedhq/dockerfilelint?branch=master)
[![Build Status](https://travis-ci.org/replicatedhq/dockerfilelint.svg?branch=master)](https://travis-ci.org/replicatedhq/dockerfilelint.svg?branch=master)
`Dockerfileint` is an npm module that analyzes a Dockerfile and looks for common traps, mistakes and helps enforce best practices:

@@ -33,2 +36,4 @@

- [x] Never run `apt-get update` without `apt-get install` on the same line
- [x] apk add commands should include a `--no-cache` flag or be paired with an `--update` flag with `rm -rf /var/cache/apk/*` in the same layer
- [ ] apk add support for --virtual flag
- [ ] handle best practices for yum operations and cleanup

@@ -35,0 +40,0 @@

@@ -5,8 +5,15 @@ var expect = require('chai').expect

describe("run_checks", function(){
describe("#aptget_commands(args)", function(){
describe("#split_commands(args)", function(){
it("splits commands separated by && into individual commands", function(){
expect(run_checks.split_commands("apt-get install -y python-pip")).to.have.length(1);
expect(run_checks.split_commands("apt-get install -y python-pip && apt-get remove -y python-pip")).to.have.length(2);
});
});
describe("#aptget_commands(commands)", function(){
it("extracts apt-get commands from longer bash command", function(){
expect(run_checks.aptget_commands("apt-get install -y python-pip")).to.have.length(1);
expect(run_checks.aptget_commands("# apt-get install -y python-pip")).to.have.length(0);
expect(run_checks.aptget_commands("echo test && apt-get install -y python-pip")).to.have.length(1);
expect(run_checks.aptget_commands("apt-get install -y python-pip && apt-get remove -y python-pip")).to.have.length(2);
expect(run_checks.aptget_commands(["apt-get install -y python-pip"])).to.have.length(1);
expect(run_checks.aptget_commands(["# apt-get install -y python-pip"])).to.have.length(0);
expect(run_checks.aptget_commands(["echo test", "apt-get install -y python-pip"])).to.have.length(1);
expect(run_checks.aptget_commands(["apt-get install -y python-pip", "apt-get remove -y python-pip"])).to.have.length(2);
});

@@ -22,5 +29,7 @@ });

describe("#aptget_with_yes(command)", function(){
describe("#aptget_hasyes(command)", function(){
it("validates that -y flag is present on apt-get (update|install|remove) commands", function(){
expect(run_checks.aptget_hasyes("apt-get install -y python-pip")).to.equal(true);
expect(run_checks.aptget_hasyes("apt-get install --yes python-pip")).to.equal(true);
expect(run_checks.aptget_hasyes("apt-get install --assume-yes python-pip")).to.equal(true);
expect(run_checks.aptget_hasyes("apt-get install python-pip")).to.equal(false);

@@ -30,3 +39,3 @@ });

describe("#aptget_install_with_norecommends(command)", function(){
describe("#aptget_hasnorecommends(command)", function(){
it("validates that --no-recommends flag is present on apt-get install commands", function(){

@@ -38,9 +47,61 @@ expect(run_checks.aptget_hasnorecommends("apt-get install -y --no-install-recommends python-pip")).to.equal(true);

describe("#aptget_install_with_rmaptlist(command)", function(){
describe("#follows_rmaptlists(command)", function(){
it("validates that a matching rm command is present on apt-get install commands", function(){
expect(run_checks.includes_rmaptlists("apt-get install -y --no--install-recommends python-pip")).to.equal(false);
expect(run_checks.includes_rmaptlists("apt-get install -y --no-install-recommends python-pip && rm -rf /var/lib/apt/lists/*")).to.equal(true);
expect(run_checks.includes_rmaptlists("apt-get install -y --no-install-recommends python-pip && rm -fr /var/lib/apt/lists/*")).to.equal(true);
expect(run_checks.follows_rmaptlists(["apt-get install -y --no--install-recommends python-pip"], 0)).to.equal(false);
expect(run_checks.follows_rmaptlists(["apt-get install -y --no-install-recommends python-pip", "rm -rf /var/lib/apt/lists/*"], 1)).to.equal(false);
expect(run_checks.follows_rmaptlists(["apt-get install -y --no-install-recommends python-pip", "rmrf -rf /var/lib/apt/lists/*"], 0)).to.equal(false);
expect(run_checks.follows_rmaptlists(["apt-get install -y --no-install-recommends python-pip", "rm -rf /var/lib/apt/lists"], 0)).to.equal(false);
expect(run_checks.follows_rmaptlists(["apt-get install -y --no-install-recommends python-pip", "rm --force /var/lib/apt/lists/*"], 0)).to.equal(false);
expect(run_checks.follows_rmaptlists(["apt-get install -y --no-install-recommends python-pip", "rm -r /var/lib/apt/lists/*"], 0)).to.equal(false);
expect(run_checks.follows_rmaptlists(["apt-get install -y --no-install-recommends python-pip", "rm -rfv /var/lib/apt/lists/*"], 0)).to.equal(true);
expect(run_checks.follows_rmaptlists(["apt-get install -y --no-install-recommends python-pip", "rm -fR /var/lib/apt/lists/*"], 0)).to.equal(true);
expect(run_checks.follows_rmaptlists(["apt-get install -y --no-install-recommends python-pip", "rm --force -r /var/lib/apt/lists/*"], 0)).to.equal(true);
expect(run_checks.follows_rmaptlists(["apt-get install -y --no-install-recommends python-pip", "rm --force --recursive /var/lib/apt/lists/*"], 0)).to.equal(true);
});
});
describe("#apk_commands(commands)", function(){
it("extracts apk commands from longer bash command", function(){
expect(run_checks.apk_commands(["apk add python-pip"])).to.have.length(1);
expect(run_checks.apk_commands(["# apk add python-pip"])).to.have.length(0);
expect(run_checks.apk_commands(["echo test", "apk add python-pip"])).to.have.length(1);
expect(run_checks.apk_commands(["apk add python-pip", "apk remove python-pip"])).to.have.length(2);
});
});
describe("#apk_subcommand(command)", function(){
it("retrieves the subcommand passed to apk", function(){
expect(run_checks.apk_subcommand("apk --no-cache add python-pip")).to.equal('add');
expect(run_checks.apk_subcommand("apk remove python-pip")).to.equal('remove');
});
});
describe("#apkadd_hasnocache(command)", function(){
it("validates that --no-cache flag is present on apk add commands", function(){
expect(run_checks.apkadd_hasnocache("apk --no-cache add python-pip")).to.equal(true);
expect(run_checks.apkadd_hasnocache("apk --update add python-pip")).to.equal(false);
});
});
describe("#apkadd_hasupdate(command)", function(){
it("validates that --no-cache flag is present on apk add commands", function(){
expect(run_checks.apkadd_hasupdate("apk --no-cache add python-pip")).to.equal(false);
expect(run_checks.apkadd_hasupdate("apk --update add python-pip")).to.equal(true);
});
});
describe("#follows_rmapkcache(command)", function(){
it("validates that a matching rm command is present on apk add commands", function(){
expect(run_checks.follows_rmapkcache(["apk add --update python-pip"], 0)).to.equal(false);
expect(run_checks.follows_rmapkcache(["apk add --update python-pip", "rm -rf /var/cache/apk"], 0)).to.equal(false);
expect(run_checks.follows_rmapkcache(["apk add --update python-pip", "rmrf -rf /var/cache/apk/*"], 0)).to.equal(false);
expect(run_checks.follows_rmapkcache(["apk add --update python-pip", "rm -rf /var/cache/apk/*"], 1)).to.equal(false);
expect(run_checks.follows_rmapkcache(["apk add --update python-pip", "rm --force /var/cache/apk/*"], 0)).to.equal(false);
expect(run_checks.follows_rmapkcache(["apk add --update python-pip", "rm -r /var/cache/apk/*"], 0)).to.equal(false);
expect(run_checks.follows_rmapkcache(["apk add --update python-pip", "rm -rfv /var/cache/apk/*"], 0)).to.equal(true);
expect(run_checks.follows_rmapkcache(["apk add --update python-pip", "rm -fR /var/cache/apk/*"], 0)).to.equal(true);
expect(run_checks.follows_rmapkcache(["apk add --update python-pip", "rm --force -r /var/cache/apk/*"], 0)).to.equal(true);
expect(run_checks.follows_rmapkcache(["apk add --update python-pip", "rm --force --recursive /var/cache/apk/*"], 0)).to.equal(true);
});
});
});

Sorry, the diff of this file is not supported yet

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