Skip to content

Commit

Permalink
Improved secure mode warnings
Browse files Browse the repository at this point in the history
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
  • Loading branch information
prabhu committed Jan 29, 2025
1 parent e6e76ca commit cd6e3fb
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 12 deletions.
21 changes: 18 additions & 3 deletions .github/workflows/repotests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,16 @@ jobs:
repository: 'bionomia/bionomia'
path: 'repotests/bionomia'
ref: '5ada8b5f4a5f68561a7195e2badc2f744dc4676e'
- uses: actions/checkout@v4
with:
repository: 'ollama/ollama'
path: 'repotests/ollama'
ref: 'v0.5.7'
- uses: actions/checkout@v4
with:
repository: 'caddyserver/caddy'
path: 'repotests/caddy'
ref: 'v2.9.1'
- uses: dtolnay/rust-toolchain@stable
- name: setup sdkman
run: |
Expand Down Expand Up @@ -415,7 +425,12 @@ jobs:
- name: repotests shiftleft-go-example
if: matrix.os != 'macos-15'
run: |
FETCH_LICENSE=false bin/cdxgen.js -p -r -t go repotests/shiftleft-go-example -o bomresults/bom-go.json --fail-on-error --export-proto
FETCH_LICENSE=false bin/cdxgen.js -p -r -t golang repotests/shiftleft-go-example -o bomresults/bom-go.json --fail-on-error --export-proto
shell: bash
- name: repotests ollama
run: |
bin/cdxgen.js -p -r -t go repotests/ollama -o bomresults/bom-ollama.json --fail-on-error
bin/cdxgen.js -p -r -t go repotests/caddy -o bomresults/bom-caddy.json --fail-on-error
shell: bash
- name: repotests go mod tests
run: |
Expand Down Expand Up @@ -582,7 +597,7 @@ jobs:
bin/cdxgen.js -r -t java repotests/shiftleft-java-example -o bomresults/1.6-bom-java.json --generate-key-and-sign --spec-version 1.6
SBOM_SIGN_ALGORITHM=RS512 SBOM_SIGN_PRIVATE_KEY=bomresults/private.key SBOM_SIGN_PUBLIC_KEY=bomresults/public.key bin/cdxgen.js -p -r -t github repotests/shiftleft-java-example -o bomresults/1.6-bom-github.json --spec-version 1.6
FETCH_LICENSE=0 bin/cdxgen.js -r -t js repotests/shiftleft-ts-example -o bomresults/1.6-bom-ts-1.json --fail-on-error --spec-version 1.6
FETCH_LICENSE=1 bin/cdxgen.js -r -t js repotests/shiftleft-ts-example --required-only -o bomresults/1.6-bom-ts-2.json --fail-on-error --spec-version 1.6
FETCH_LICENSE=1 bin/cdxgen.js -r -t javascript repotests/shiftleft-ts-example --required-only -o bomresults/1.6-bom-ts-2.json --fail-on-error --spec-version 1.6
FETCH_LICENSE=true bin/cdxgen.js -r -t csharp repotests/vulnerable_net_core -o bomresults/1.6-bom-csharp2.json --spec-version 1.6
FETCH_LICENSE=false bin/cdxgen.js -r repotests/Goatly.NET -o bomresults/1.6-bom-csharp3.json --spec-version 1.6
FETCH_LICENSE=true bin/cdxgen.js -r -t python repotests/DjanGoat -o bomresults/1.6-bom-python.json --fail-on-error --spec-version 1.6
Expand All @@ -593,7 +608,7 @@ jobs:
bin/cdxgen.js -r -t java repotests/shiftleft-java-example -o bomresults/1.4-bom-java.json --generate-key-and-sign --spec-version 1.4
SBOM_SIGN_ALGORITHM=RS512 SBOM_SIGN_PRIVATE_KEY=bomresults/private.key SBOM_SIGN_PUBLIC_KEY=bomresults/public.key bin/cdxgen.js -p -r -t github repotests/shiftleft-java-example -o bomresults/1.4-bom-github.json --spec-version 1.4
FETCH_LICENSE=0 bin/cdxgen.js -r -t js repotests/shiftleft-ts-example -o bomresults/1.4-bom-ts-1.json --fail-on-error --spec-version 1.4
FETCH_LICENSE=1 bin/cdxgen.js -r -t js repotests/shiftleft-ts-example --required-only -o bomresults/1.4-bom-ts-2.json --fail-on-error --spec-version 1.4
FETCH_LICENSE=1 bin/cdxgen.js -r -t javascript repotests/shiftleft-ts-example --required-only -o bomresults/1.4-bom-ts-2.json --fail-on-error --spec-version 1.4
FETCH_LICENSE=true bin/cdxgen.js -r -t csharp repotests/vulnerable_net_core -o bomresults/1.4-bom-csharp2.json --spec-version 1.4
FETCH_LICENSE=false bin/cdxgen.js -r repotests/Goatly.NET -o bomresults/1.4-bom-csharp3.json --spec-version 1.4
FETCH_LICENSE=true bin/cdxgen.js -r -t python repotests/DjanGoat -o bomresults/1.4-bom-python.json --fail-on-error --spec-version 1.4
Expand Down
36 changes: 33 additions & 3 deletions bin/cdxgen.js
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ if (process.env.GLOBAL_AGENT_HTTP_PROXY || process.env.HTTP_PROXY) {
globalAgent.bootstrap();
}

const filePath = args._[0] || process.cwd();
const filePath = resolve(args._[0]) || process.cwd();
if (!args.projectName) {
if (filePath !== ".") {
args.projectName = basename(filePath);
Expand All @@ -411,6 +411,10 @@ const options = Object.assign({}, args, {
noBabel: args.noBabel || args.babel === false,
project: args.projectId,
deep: args.deep || args.evidence,
output:
isSecureMode && args.output === "bom.json"
? resolve(join(filePath, args.output))
: args.output,
});

if (process.argv[1].includes("cbom")) {
Expand All @@ -419,6 +423,13 @@ if (process.argv[1].includes("cbom")) {
options.specVersion = 1.6;
options.deep = true;
}
if (process.argv[1].includes("cdxgen-secure")) {
console.log(
"NOTE: Secure mode only restricts cdxgen from performing certain activities such as package installation. It does not provide security guarantees in the presence of malicious code.",
);
options.installDeps = false;
process.env.CDXGEN_SECURE_MODE = true;
}
if (options.standard) {
options.specVersion = 1.6;
}
Expand Down Expand Up @@ -542,10 +553,29 @@ applyAdvancedOptions(options);
* @returns
*/
const checkPermissions = (filePath, options) => {
const fullFilePath = resolve(filePath);
if (process.getuid() === 0 && process.env?.CDXGEN_IN_CONTAINER !== "true") {
console.log(
"\x1b[1;35mSECURE MODE: DO NOT run cdxgen with root privileges.\x1b[0m",
);
}
if (!process.permission) {
if (isSecureMode) {
console.log(
"\x1b[1;35mSecure mode requires permission-related arguments. These can be passed as CLI arguments directly to the node runtime or set the NODE_OPTIONS environment variable as shown below.\x1b[0m",
);
const nodeOptionsVal = `--permission --allow-fs-read="${getTmpDir()}/*" --allow-fs-write="${getTmpDir()}/*" --allow-fs-read="${fullFilePath}/*" --allow-fs-write="${options.output}" --allow-child-process`;
console.log(
`${isWin ? "$env:" : "export "}NODE_OPTIONS='${nodeOptionsVal}'`,
);
if (process.env?.CDXGEN_IN_CONTAINER !== "true") {
console.log(
"TIP: Run cdxgen using the secure container image 'ghcr.io/cyclonedx/cdxgen-secure' for best experience.",
);
}
}
return true;
}
const fullFilePath = resolve(filePath);
// Secure mode checks
if (isSecureMode) {
if (process.permission.has("fs.read", "*")) {
Expand All @@ -565,7 +595,7 @@ const checkPermissions = (filePath, options) => {
}
if (filePath !== fullFilePath) {
console.log(
`\x1b[1;35mSECURE MODE: Invoke cdxgen with an absolute path to improve security. Use ${fullFilePath} instead of ${filePath}.\x1b[0m`,
`\x1b[1;35mSECURE MODE: Invoke cdxgen with an absolute path to improve security. Use '${fullFilePath}' instead of '${filePath}'\x1b[0m`,
);
if (fullFilePath.includes(" ")) {
console.log(
Expand Down
10 changes: 8 additions & 2 deletions lib/helpers/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,13 +279,18 @@ export function validateProps(bomJson) {
const errorList = [];
const warningsList = [];
let isWorkspaceMode = false;
let lacksProperties = false;
let lacksEvidence = false;
if (bomJson?.components) {
for (const comp of bomJson.components) {
if (!["library", "framework"].includes(comp.type)) {
continue;
}
if (!comp.properties) {
warningsList.push(`${comp["bom-ref"]} lacks properties.`);
if (!lacksProperties) {
warningsList.push(`${comp["bom-ref"]} lacks properties.`);
lacksProperties = true;
}
} else {
let srcFilePropFound = false;
let workspacePropFound = false;
Expand All @@ -312,7 +317,8 @@ export function validateProps(bomJson) {
warningsList.push(`${comp["bom-ref"]} lacks SrcFile property.`);
}
}
if (!comp.evidence) {
if (!comp.evidence && !lacksEvidence) {
lacksEvidence = true;
warningsList.push(`${comp["bom-ref"]} lacks evidence.`);
}
}
Expand Down
47 changes: 45 additions & 2 deletions lib/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import url from "node:url";
import bodyParser from "body-parser";
import connect from "connect";
import { createBom, submitBom } from "../cli/index.js";
import { getTmpDir } from "../helpers/utils.js";
import { getTmpDir, isSecureMode } from "../helpers/utils.js";
import { postProcess } from "../stages/postgen/postgen.js";

import compression from "compression";
Expand Down Expand Up @@ -131,7 +131,32 @@ const configureServer = (cdxgenServer) => {
};

const start = (options) => {
console.log("Listening on", options.serverHost, options.serverPort);
console.log(
"Listening on",
options.serverHost,
options.serverPort,
"without authentication!",
);
if (["0.0.0.0", "::", "::/128", "::/0"].includes(options.serverHost)) {
console.log("Exposing cdxgen server on all IP address is a security risk!");
if (isSecureMode) {
process.exit(1);
}
}
if (+options.serverPort < 1024) {
console.log(
"Running cdxgen server with a privileged port is a security risk!",
);
if (isSecureMode) {
process.exit(1);
}
}
if (process.getuid() === 0 && process.env?.CDXGEN_IN_CONTAINER !== "true") {
console.log("Running cdxgen server as root is a security risk!");
if (isSecureMode) {
process.exit(1);
}
}
const cdxgenServer = http
.createServer(app)
.listen(options.serverPort, options.serverHost);
Expand Down Expand Up @@ -163,11 +188,29 @@ const start = (options) => {
if (filePath.startsWith("http") || filePath.startsWith("git")) {
srcDir = gitClone(filePath, reqOptions.gitBranch);
cleanup = true;
} else if (srcDir !== path.resolve(srcDir)) {
console.log(
`Invoke the API with an absolute path '${path.resolve(srcDir)}' instead of '${srcDir}' to reduce security risks.`,
);
if (isSecureMode) {
res.writeHead(500, { "Content-Type": "application/json" });
return res.end(
JSON.stringify({
error: `Provide an absolute path instead of '${srcDir}'`,
details: "Relative paths are not supported in secure mode.",
}),
);
}
}
console.log("Generating SBOM for", srcDir);
let bomNSData = (await createBom(srcDir, reqOptions)) || {};
bomNSData = postProcess(bomNSData, reqOptions);
if (reqOptions.serverUrl && reqOptions.apiKey) {
if (isSecureMode && !reqOptions.serverUrl?.startsWith("https://")) {
console.log(
"Dependency Track API server is used with a non-https url, which poses a security risk.",
);
}
console.log(
`Publishing SBOM ${reqOptions.projectName} to Dependency Track`,
reqOptions.serverUrl,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"types": "./types/index.d.ts",
"bin": {
"cdxgen": "bin/cdxgen.js",
"cdxgen-secure": "bin/cdxgen.js",
"obom": "bin/cdxgen.js",
"cbom": "bin/cdxgen.js",
"cdxi": "bin/repl.js",
Expand Down
2 changes: 1 addition & 1 deletion types/lib/helpers/validator.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion types/lib/server/server.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit cd6e3fb

Please sign in to comment.