@synopackageland/cli 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +44 -1
- package/dist/build-spk/generator.d.ts +3 -2
- package/dist/build-spk/generator.js +6 -850
- package/dist/build-spk/resource-worker.d.ts +2 -14
- package/dist/build-spk/resource-worker.js +3 -166
- package/dist/cli.js +208 -56
- package/dist/compose/analyze.d.ts +3 -61
- package/dist/compose/analyze.js +35 -776
- package/dist/compose/env.d.ts +5 -3
- package/dist/compose/env.js +17 -25
- package/dist/compose/runner.js +23 -6
- package/dist/contract/app-launch.d.ts +11 -0
- package/dist/contract/app-launch.js +18 -0
- package/dist/contract/web-launch.d.ts +3 -13
- package/dist/contract/web-launch.js +14 -81
- package/dist/ctl/cli.d.ts +36 -0
- package/dist/ctl/cli.js +218 -0
- package/dist/ctl/descriptor.d.ts +81 -0
- package/dist/ctl/descriptor.js +93 -0
- package/dist/ctl/dsm-auth.d.ts +10 -0
- package/dist/ctl/dsm-auth.js +30 -0
- package/dist/ctl/errors.d.ts +17 -0
- package/dist/ctl/errors.js +32 -0
- package/dist/ctl/flows.d.ts +11 -0
- package/dist/ctl/flows.js +52 -0
- package/dist/ctl/input.d.ts +20 -0
- package/dist/ctl/input.js +128 -0
- package/dist/ctl/json.d.ts +2 -0
- package/dist/ctl/json.js +38 -0
- package/dist/ctl/redaction.d.ts +15 -0
- package/dist/ctl/redaction.js +198 -0
- package/dist/ctl/service.d.ts +38 -0
- package/dist/ctl/service.js +156 -0
- package/dist/ctl/store.d.ts +51 -0
- package/dist/ctl/store.js +93 -0
- package/dist/ctl/transfers.d.ts +14 -0
- package/dist/ctl/transfers.js +74 -0
- package/dist/ctl/transport.d.ts +11 -0
- package/dist/ctl/transport.js +79 -0
- package/dist/deploy/deploy.d.ts +4 -3
- package/dist/deploy/deploy.js +5 -210
- package/dist/deploy/plan.js +26 -7
- package/dist/dev.js +5 -4
- package/dist/error-registry.js +1020 -831
- package/dist/info.js +120 -28
- package/dist/init.js +188 -64
- package/dist/inspect.js +21 -6
- package/dist/runtime/executor-digests.json +1 -12
- package/dist/skill.js +51 -9
- package/dist/standalone/api-description-inspect.d.ts +4 -0
- package/dist/standalone/api-description-inspect.js +12 -0
- package/dist/standalone/api-description.d.ts +7 -0
- package/dist/standalone/api-description.js +73 -0
- package/dist/standalone/build.d.ts +3 -0
- package/dist/standalone/build.js +140 -33
- package/dist/standalone/compose-errors.d.ts +3 -0
- package/dist/standalone/compose-errors.js +243 -0
- package/dist/standalone/compose.d.ts +97 -10
- package/dist/standalone/compose.js +306 -75
- package/dist/standalone/daemon.js +111 -29
- package/dist/standalone/dsm-window.js +55 -9
- package/dist/standalone/identity-gateway.d.ts +9 -2
- package/dist/standalone/identity-gateway.js +282 -66
- package/dist/standalone/inspect-spk.d.ts +15 -0
- package/dist/standalone/inspect-spk.js +714 -128
- package/dist/standalone/lifecycle.js +124 -42
- package/dist/standalone/manifest.d.ts +10 -1
- package/dist/standalone/manifest.js +308 -87
- package/dist/standalone/resource.js +6 -2
- package/dist/standalone/spk.d.ts +4 -1
- package/dist/standalone/spk.js +139 -39
- package/dist/template-paths.d.ts +0 -2
- package/dist/template-paths.js +0 -5
- package/dist/test-command.d.ts +1 -1
- package/dist/test-command.js +1 -1
- package/dist/validate.d.ts +3 -3
- package/dist/validate.js +32 -140
- package/package.json +5 -3
- package/schema/synology-app.schema.json +1 -1
- package/schema/synopkgland-api-v1.schema.json +423 -0
- package/skills/SKILL.md +34 -1
- package/skills/reference/compose.md +349 -0
- package/skills/reference/create.md +26 -4
- package/skills/reference/ctl.md +41 -0
- package/skills/reference/deploy.md +1 -1
- package/skills/reference/files.md +4 -0
- package/skills/reference/identity-gateway.md +8 -138
- package/skills/reference/portal.md +31 -0
- package/skills/reference/ui-vue.md +31 -0
- package/skills/reference/wrap.md +10 -30
- package/assets/go/daemon/fixture_linux_test.go +0 -15
- package/assets/go/daemon/fixture_other_test.go +0 -7
- package/assets/go/daemon/go.mod +0 -5
- package/assets/go/daemon/main.go +0 -636
- package/assets/go/daemon/main_test.go +0 -315
- package/assets/go/daemon/network.go +0 -107
- package/assets/go/daemon/network_test.go +0 -86
- package/assets/go/daemon/process_linux.go +0 -55
- package/assets/go/daemon/process_linux_test.go +0 -161
- package/assets/go/daemon/process_other.go +0 -14
- package/assets/go/daemon/testdata/child.go +0 -44
- package/assets/go/gateway/auth.go +0 -101
- package/assets/go/gateway/auth_test.go +0 -216
- package/assets/go/gateway/cgi.go +0 -133
- package/assets/go/gateway/cgi_test.go +0 -156
- package/assets/go/gateway/csrf.go +0 -73
- package/assets/go/gateway/csrf_test.go +0 -155
- package/assets/go/gateway/directory.go +0 -108
- package/assets/go/gateway/directory_test.go +0 -202
- package/assets/go/gateway/gateway.go +0 -412
- package/assets/go/gateway/gateway_test.go +0 -676
- package/assets/go/gateway/go.mod +0 -5
- package/assets/go/gateway/groups.go +0 -66
- package/assets/go/gateway/main.go +0 -45
- package/dist/build-spk/gateway.d.ts +0 -31
- package/dist/build-spk/gateway.js +0 -60
- package/dist/compose/privilege.d.ts +0 -14
- package/dist/compose/privilege.js +0 -102
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"bufio"
|
|
5
|
-
"context"
|
|
6
|
-
"fmt"
|
|
7
|
-
"os"
|
|
8
|
-
"slices"
|
|
9
|
-
"strings"
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
// groupsOf reads local group files fresh on each request. Directory accounts
|
|
13
|
-
// use NSS afresh; DSM provider caching may delay directory membership changes.
|
|
14
|
-
// DSM keeps no member list for the group
|
|
15
|
-
// that is an account's primary group (users), so membership is the member
|
|
16
|
-
// list or the primary gid from the passwd file. Accounts absent from passwd use
|
|
17
|
-
// DSM NSS; no local member-list fallback is permitted on lookup failure.
|
|
18
|
-
func (a *dsmAuthenticator) groupsOf(ctx context.Context, user string) ([]string, error) {
|
|
19
|
-
primary, err := a.primaryGID(user)
|
|
20
|
-
if err != nil {
|
|
21
|
-
return nil, err
|
|
22
|
-
}
|
|
23
|
-
if primary == "" {
|
|
24
|
-
if a.directoryGroups == nil {
|
|
25
|
-
return nil, fmt.Errorf("directory group lookup unavailable")
|
|
26
|
-
}
|
|
27
|
-
return a.directoryGroups(ctx, user)
|
|
28
|
-
}
|
|
29
|
-
f, err := os.Open(a.groupFile)
|
|
30
|
-
if err != nil {
|
|
31
|
-
return nil, fmt.Errorf("read groups: %w", err)
|
|
32
|
-
}
|
|
33
|
-
defer f.Close()
|
|
34
|
-
var groups []string
|
|
35
|
-
scanner := bufio.NewScanner(f)
|
|
36
|
-
for scanner.Scan() {
|
|
37
|
-
fields := strings.Split(scanner.Text(), ":")
|
|
38
|
-
if len(fields) == 4 && (fields[2] == primary || slices.Contains(strings.Split(fields[3], ","), user)) {
|
|
39
|
-
groups = append(groups, fields[0])
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if err := scanner.Err(); err != nil {
|
|
43
|
-
return nil, fmt.Errorf("read groups: %w", err)
|
|
44
|
-
}
|
|
45
|
-
return groups, nil
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// primaryGID is the account's gid in the passwd file; "" when the account has
|
|
49
|
-
// no local entry, which requires a fresh directory lookup.
|
|
50
|
-
func (a *dsmAuthenticator) primaryGID(user string) (string, error) {
|
|
51
|
-
f, err := os.Open(a.passwdFile)
|
|
52
|
-
if err != nil {
|
|
53
|
-
return "", fmt.Errorf("read accounts: %w", err)
|
|
54
|
-
}
|
|
55
|
-
defer f.Close()
|
|
56
|
-
scanner := bufio.NewScanner(f)
|
|
57
|
-
for scanner.Scan() {
|
|
58
|
-
if fields := strings.Split(scanner.Text(), ":"); len(fields) >= 4 && fields[0] == user {
|
|
59
|
-
return fields[3], nil
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if err := scanner.Err(); err != nil {
|
|
63
|
-
return "", fmt.Errorf("read accounts: %w", err)
|
|
64
|
-
}
|
|
65
|
-
return "", nil
|
|
66
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// Command gateway is the DSM-authenticated front door of a container package.
|
|
2
|
-
// DSM runs it as a CGI (ui/cgi/gateway.cgi in the package) under the package
|
|
3
|
-
// account, once per request.
|
|
4
|
-
package main
|
|
5
|
-
|
|
6
|
-
import (
|
|
7
|
-
"bytes"
|
|
8
|
-
"encoding/json"
|
|
9
|
-
"fmt"
|
|
10
|
-
"log"
|
|
11
|
-
"net/http/cgi"
|
|
12
|
-
"os"
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
// Set by `synopkgland build-spk` through -ldflags -X; never read from a request.
|
|
16
|
-
var (
|
|
17
|
-
packageID = "dev"
|
|
18
|
-
configJSON = ""
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
func main() {
|
|
22
|
-
log.SetFlags(0)
|
|
23
|
-
log.SetPrefix(packageID + ": ")
|
|
24
|
-
if os.Getenv("GATEWAY_INTERFACE") == "" {
|
|
25
|
-
fmt.Fprintf(os.Stderr, "%s: this program runs as the package's DSM CGI; open the app from the DSM main menu.\n", packageID)
|
|
26
|
-
os.Exit(2)
|
|
27
|
-
}
|
|
28
|
-
var cfg config
|
|
29
|
-
decoder := json.NewDecoder(bytes.NewReader([]byte(configJSON)))
|
|
30
|
-
decoder.DisallowUnknownFields()
|
|
31
|
-
if err := decoder.Decode(&cfg); err != nil {
|
|
32
|
-
log.Fatalf("invalid gateway configuration: %v", err)
|
|
33
|
-
}
|
|
34
|
-
g, err := newGateway(cfg, newDSMAuthenticator())
|
|
35
|
-
if err != nil {
|
|
36
|
-
log.Fatal(err)
|
|
37
|
-
}
|
|
38
|
-
req, err := cgi.Request()
|
|
39
|
-
if err != nil {
|
|
40
|
-
log.Fatal(err)
|
|
41
|
-
}
|
|
42
|
-
if err := serveCGI(g, req, os.Stdin, os.Stdout); err != nil {
|
|
43
|
-
log.Fatal(err)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { type CommandRunner } from "../standalone/build.js";
|
|
2
|
-
import type { AppContract } from "../types.js";
|
|
3
|
-
import type { NativeArchFamily } from "./dsm-arch.js";
|
|
4
|
-
export { gatewayEntryPath } from "../contract/web-launch.js";
|
|
5
|
-
/** Where the gateway CGI sits in the package payload; DSM serves `ui/` under /webman/3rdparty/<package>/. */
|
|
6
|
-
export declare const GATEWAY_CGI_PAYLOAD = "ui/cgi/gateway.cgi";
|
|
7
|
-
/** Readable copy of the baked-in configuration, for inspection; the CGI does not read it. */
|
|
8
|
-
export declare const GATEWAY_CONFIG_PAYLOAD = "gateway.json";
|
|
9
|
-
export type GatewayErrorCode = "GATEWAY_GO_MISSING" | "GATEWAY_BUILD_FAILED";
|
|
10
|
-
export interface GatewayConfig {
|
|
11
|
-
upstream: string;
|
|
12
|
-
access: "users" | "admins" | "groups";
|
|
13
|
-
groups?: string[];
|
|
14
|
-
}
|
|
15
|
-
/** The access rule and container address the gateway enforces; undefined for an App without one. */
|
|
16
|
-
export declare function gatewayConfig(contract: AppContract, adminport: number): GatewayConfig | undefined;
|
|
17
|
-
/** Compiles the fixed gateway module for `arch`; the result is a static ELF or a coded failure. */
|
|
18
|
-
export declare function buildGatewayBinary(input: {
|
|
19
|
-
packageId: string;
|
|
20
|
-
arch: NativeArchFamily;
|
|
21
|
-
config: GatewayConfig;
|
|
22
|
-
work: string;
|
|
23
|
-
run?: CommandRunner;
|
|
24
|
-
}): Promise<{
|
|
25
|
-
binary: string;
|
|
26
|
-
} | {
|
|
27
|
-
error: GatewayErrorCode;
|
|
28
|
-
detail: string;
|
|
29
|
-
}>;
|
|
30
|
-
/** Puts the CGI (mode 0755) and its readable configuration into the package payload. */
|
|
31
|
-
export declare function writeGatewayPayload(packageDir: string, binary: string, config: GatewayConfig): void;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { goChildEnv } from "../runtime/go-env.js";
|
|
2
|
-
import { chmodSync, copyFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { checkCgiBinary } from "../standalone/binary.js";
|
|
5
|
-
import { runCommand } from "../standalone/build.js";
|
|
6
|
-
import { resolvedWebAccess } from "../contract/web-launch.js";
|
|
7
|
-
import { goAssetDir } from "../template-paths.js";
|
|
8
|
-
import { PINNED_GO_HINT, pinnedGoBuildEnv, pinnedGoProblem } from "./go-toolchain.js";
|
|
9
|
-
export { gatewayEntryPath } from "../contract/web-launch.js";
|
|
10
|
-
/** Where the gateway CGI sits in the package payload; DSM serves `ui/` under /webman/3rdparty/<package>/. */
|
|
11
|
-
export const GATEWAY_CGI_PAYLOAD = "ui/cgi/gateway.cgi";
|
|
12
|
-
/** Readable copy of the baked-in configuration, for inspection; the CGI does not read it. */
|
|
13
|
-
export const GATEWAY_CONFIG_PAYLOAD = "gateway.json";
|
|
14
|
-
const ACCESS = { "dsm-users": "users", "dsm-admins": "admins", "dsm-groups": "groups" };
|
|
15
|
-
/** The access rule and container address the gateway enforces; undefined for an App without one. */
|
|
16
|
-
export function gatewayConfig(contract, adminport) {
|
|
17
|
-
const access = resolvedWebAccess(contract);
|
|
18
|
-
if (!access || access.mode === "public") {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
upstream: `127.0.0.1:${adminport}`,
|
|
23
|
-
access: ACCESS[access.mode],
|
|
24
|
-
...(access.mode === "dsm-groups" ? { groups: access.groups } : {}),
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
/** Compiles the fixed gateway module for `arch`; the result is a static ELF or a coded failure. */
|
|
28
|
-
export async function buildGatewayBinary(input) {
|
|
29
|
-
const out = join(input.work, `gateway-${input.arch}.cgi`);
|
|
30
|
-
const ldflags = ["-s", "-w", "-buildid=", `-X main.packageID=${input.packageId}`, `-X 'main.configJSON=${JSON.stringify(input.config)}'`].join(" ");
|
|
31
|
-
const result = await (input.run ?? runCommand)("go", ["build", "-trimpath", "-buildvcs=false", "-ldflags", ldflags, "-o", out, "."], {
|
|
32
|
-
cwd: goAssetDir("gateway"),
|
|
33
|
-
env: goChildEnv({ ...process.env, ...pinnedGoBuildEnv(input.arch) }),
|
|
34
|
-
});
|
|
35
|
-
if (result.missing) {
|
|
36
|
-
return { error: "GATEWAY_GO_MISSING", detail: `The go command was not found. Building the DSM verification gateway needs Go 1.21 or later (https://go.dev/dl/). ${PINNED_GO_HINT}` };
|
|
37
|
-
}
|
|
38
|
-
if (result.status !== 0) {
|
|
39
|
-
return { error: "GATEWAY_BUILD_FAILED", detail: `go build (${input.arch}) failed. ${PINNED_GO_HINT}\n${result.stderr}` };
|
|
40
|
-
}
|
|
41
|
-
const problem = checkCgiBinary(input.work, out, input.arch);
|
|
42
|
-
if (problem) {
|
|
43
|
-
return { error: "GATEWAY_BUILD_FAILED", detail: problem.message };
|
|
44
|
-
}
|
|
45
|
-
const toolchainProblem = pinnedGoProblem(out);
|
|
46
|
-
if (toolchainProblem) {
|
|
47
|
-
return { error: "GATEWAY_BUILD_FAILED", detail: toolchainProblem };
|
|
48
|
-
}
|
|
49
|
-
return { binary: out };
|
|
50
|
-
}
|
|
51
|
-
/** Puts the CGI (mode 0755) and its readable configuration into the package payload. */
|
|
52
|
-
export function writeGatewayPayload(packageDir, binary, config) {
|
|
53
|
-
const cgi = join(packageDir, GATEWAY_CGI_PAYLOAD);
|
|
54
|
-
mkdirSync(join(cgi, ".."), { recursive: true });
|
|
55
|
-
copyFileSync(binary, cgi);
|
|
56
|
-
chmodSync(cgi, 0o755);
|
|
57
|
-
const configPath = join(packageDir, GATEWAY_CONFIG_PAYLOAD);
|
|
58
|
-
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
59
|
-
chmodSync(configPath, 0o644);
|
|
60
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { AppContract, ComposeDocument } from "../types.js";
|
|
2
|
-
export declare const PRIVILEGE_FLAGS: readonly ["privileged", "host_network", "host_pid", "host_ipc", "devices", "cap_add"];
|
|
3
|
-
export type PrivilegeFlag = (typeof PRIVILEGE_FLAGS)[number];
|
|
4
|
-
export interface PrivilegeFinding {
|
|
5
|
-
flag: PrivilegeFlag;
|
|
6
|
-
code: string;
|
|
7
|
-
serviceName: string;
|
|
8
|
-
path: string;
|
|
9
|
-
message: string;
|
|
10
|
-
suggestion: string;
|
|
11
|
-
}
|
|
12
|
-
export declare function declaredPrivilegeFlags(contract: AppContract): Set<PrivilegeFlag>;
|
|
13
|
-
export declare function scanComposePrivilege(compose: ComposeDocument): PrivilegeFinding[];
|
|
14
|
-
export declare function undeclaredPrivilegeFindings(compose: ComposeDocument, declared: ReadonlySet<PrivilegeFlag>): PrivilegeFinding[];
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
export const PRIVILEGE_FLAGS = [
|
|
2
|
-
"privileged",
|
|
3
|
-
"host_network",
|
|
4
|
-
"host_pid",
|
|
5
|
-
"host_ipc",
|
|
6
|
-
"devices",
|
|
7
|
-
"cap_add",
|
|
8
|
-
];
|
|
9
|
-
const FLAG_ERRORS = {
|
|
10
|
-
privileged: {
|
|
11
|
-
code: "COMPOSE_PRIVILEGED_FORBIDDEN",
|
|
12
|
-
message: "Compose uses a privileged container but the contract does not declare privilege.privileged.",
|
|
13
|
-
suggestion: "Add privilege.privileged: true to synology-app.yaml, or remove privileged: true.",
|
|
14
|
-
},
|
|
15
|
-
host_network: {
|
|
16
|
-
code: "COMPOSE_HOST_NETWORK_FORBIDDEN",
|
|
17
|
-
message: "Compose uses host network but the contract does not declare privilege.host_network.",
|
|
18
|
-
suggestion: "Add privilege.host_network: true to synology-app.yaml, or use a controlled network mode.",
|
|
19
|
-
},
|
|
20
|
-
host_pid: {
|
|
21
|
-
code: "COMPOSE_HOST_PID_FORBIDDEN",
|
|
22
|
-
message: "Compose uses host PID but the contract does not declare privilege.host_pid.",
|
|
23
|
-
suggestion: "Add privilege.host_pid: true to synology-app.yaml, or remove pid: host.",
|
|
24
|
-
},
|
|
25
|
-
host_ipc: {
|
|
26
|
-
code: "COMPOSE_HOST_IPC_FORBIDDEN",
|
|
27
|
-
message: "Compose uses host IPC but the contract does not declare privilege.host_ipc.",
|
|
28
|
-
suggestion: "Add privilege.host_ipc: true to synology-app.yaml, or remove ipc: host.",
|
|
29
|
-
},
|
|
30
|
-
devices: {
|
|
31
|
-
code: "COMPOSE_DEVICES_FORBIDDEN",
|
|
32
|
-
message: "Compose mounts devices but the contract does not declare privilege.devices.",
|
|
33
|
-
suggestion: "Add privilege.devices: true to synology-app.yaml, or remove devices.",
|
|
34
|
-
},
|
|
35
|
-
cap_add: {
|
|
36
|
-
code: "COMPOSE_CAP_ADD_FORBIDDEN",
|
|
37
|
-
message: "Compose uses extra cap_add but the contract does not declare privilege.cap_add.",
|
|
38
|
-
suggestion: "Add privilege.cap_add: true to synology-app.yaml, or remove cap_add.",
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
export function declaredPrivilegeFlags(contract) {
|
|
42
|
-
const raw = contract.privilege;
|
|
43
|
-
const flags = new Set();
|
|
44
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
45
|
-
return flags;
|
|
46
|
-
}
|
|
47
|
-
const record = raw;
|
|
48
|
-
for (const flag of PRIVILEGE_FLAGS) {
|
|
49
|
-
if (record[flag] === true) {
|
|
50
|
-
flags.add(flag);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return flags;
|
|
54
|
-
}
|
|
55
|
-
export function scanComposePrivilege(compose) {
|
|
56
|
-
const findings = [];
|
|
57
|
-
if (!compose.services) {
|
|
58
|
-
return findings;
|
|
59
|
-
}
|
|
60
|
-
for (const [serviceName, service] of Object.entries(compose.services)) {
|
|
61
|
-
const basePath = `services.${serviceName}`;
|
|
62
|
-
const add = (flag, path) => {
|
|
63
|
-
const spec = FLAG_ERRORS[flag];
|
|
64
|
-
findings.push({
|
|
65
|
-
flag,
|
|
66
|
-
code: spec.code,
|
|
67
|
-
serviceName,
|
|
68
|
-
path,
|
|
69
|
-
message: spec.message,
|
|
70
|
-
suggestion: spec.suggestion,
|
|
71
|
-
});
|
|
72
|
-
};
|
|
73
|
-
if (service.privileged === true) {
|
|
74
|
-
add("privileged", `${basePath}.privileged`);
|
|
75
|
-
}
|
|
76
|
-
if (service.network_mode === "host") {
|
|
77
|
-
add("host_network", `${basePath}.network_mode`);
|
|
78
|
-
}
|
|
79
|
-
if (service.pid === "host") {
|
|
80
|
-
add("host_pid", `${basePath}.pid`);
|
|
81
|
-
}
|
|
82
|
-
if (service.ipc === "host") {
|
|
83
|
-
add("host_ipc", `${basePath}.ipc`);
|
|
84
|
-
}
|
|
85
|
-
if (hasEntries(service.devices)) {
|
|
86
|
-
add("devices", `${basePath}.devices`);
|
|
87
|
-
}
|
|
88
|
-
if (hasEntries(service.cap_add)) {
|
|
89
|
-
add("cap_add", `${basePath}.cap_add`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return findings;
|
|
93
|
-
}
|
|
94
|
-
export function undeclaredPrivilegeFindings(compose, declared) {
|
|
95
|
-
return scanComposePrivilege(compose).filter((item) => !declared.has(item.flag));
|
|
96
|
-
}
|
|
97
|
-
function hasEntries(value) {
|
|
98
|
-
if (Array.isArray(value)) {
|
|
99
|
-
return value.length > 0;
|
|
100
|
-
}
|
|
101
|
-
return typeof value === "string" && value.trim() !== "";
|
|
102
|
-
}
|