All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.iac.docker.checks.utils.Chmod Maven / Gradle / Ivy

/*
 * SonarQube IaC Plugin
 * Copyright (C) 2021-2023 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.iac.docker.checks.utils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.annotation.Nullable;
import org.sonar.iac.docker.symbols.ArgumentResolution;
import org.sonar.iac.docker.tree.api.Argument;

/**
 * Represent chmod call instruction in RUN Arguments, with parsed permissions ready to be checked
 */
public class Chmod {
  private static final Pattern CHMOD_OPTIONS_PATTERN = Pattern.compile("-[a-zA-Z]|--[a-zA-Z-]++");
  private static final String NUMERIC = "(?[0-7]{1,4})";
  private static final String ALPHANUMERIC = "[ugoa]*+[=+-][rwxXstugo]++";
  private static final String ALPHANUMERICS = "(?(?:" + ALPHANUMERIC + ",?+)++)";
  private static final Pattern PERMISSIONS_PATTERN = Pattern.compile(NUMERIC + "|" + ALPHANUMERICS);

  public final Argument chmodArg;
  public final Argument permissionsArg;
  public final Permission permissions;

  public Chmod(@Nullable Argument chmodArg, @Nullable Argument permissionsArg, String permissions) {
    this.chmodArg = chmodArg;
    this.permissionsArg = permissionsArg;
    this.permissions = parsePermissions(permissions);
  }

  private static Permission parsePermissions(String permissions) {
    Matcher matcher = PERMISSIONS_PATTERN.matcher(permissions);
    if (matcher.find()) {
      if (matcher.group("numeric") != null) {
        return Permission.fromNumeric(matcher.group("numeric"));
      } else {
        return Permission.fromAlphanumeric(matcher.group("alphanumeric"));
      }
    } else {
      return Permission.empty();
    }
  }

  public static List extractChmodsFromArguments(List arguments) {
    List chmods = new ArrayList<>();
    List argumentsStrings = arguments.stream()
      .map(arg -> ArgumentResolution.of(arg).value())
      .collect(Collectors.toList());

    List chmodIndexes = findChmodIndexes(argumentsStrings);
    for (Integer chmodIndex : chmodIndexes) {
      Integer indexPermissions = skipOptions(chmodIndex, argumentsStrings);
      if (indexPermissions != null) {
        chmods.add(new Chmod(arguments.get(chmodIndex), arguments.get(indexPermissions), argumentsStrings.get(indexPermissions)));
      }
    }

    return chmods;
  }

  private static List findChmodIndexes(List arguments) {
    return IntStream.range(0, arguments.size())
      .filter(i -> "chmod".equals(arguments.get(i)))
      .boxed()
      .collect(Collectors.toList());
  }

  private static Integer skipOptions(int index, List arguments) {
    do {
      index++;
    } while (index < arguments.size() && arguments.get(index) != null && CHMOD_OPTIONS_PATTERN.matcher(arguments.get(index)).matches());
    return index < arguments.size() ? index : null;
  }

  public boolean hasPermission(String right) {
    return permissions.rights.contains(right);
  }

  /**
   * Class dedicated to store permissions in the chmod way : man chmod
   */
  public static class Permission {
    private Permission() {
    }

    // Store permissions at the following format : +
    // Example : "u+w" -> user has write permission
    Set rights = new HashSet<>();

    static Permission empty() {
      return new Permission();
    }

    public static Permission fromAlphanumeric(String alphanumerics) {
      Permission chmodRight = new Permission();
      for (String alphanumeric : alphanumerics.split(",")) {
        if (alphanumeric.contains("-")) {
          continue;
        }
        String[] split = alphanumeric.split("[+=]");
        chmodRight.addRights(split[0], split[1]);
      }
      return chmodRight;
    }

    public static Permission fromNumeric(String numeric) {
      Permission chmodRight = new Permission();
      numeric = ("0000" + numeric).substring(numeric.length());
      chmodRight.addSet(numeric.charAt(0));
      chmodRight.addRight(numeric.charAt(1), 'u');
      chmodRight.addRight(numeric.charAt(2), 'g');
      chmodRight.addRight(numeric.charAt(3), 'o');
      return chmodRight;
    }

    private void addRight(char digit, char target) {
      int value = digit - '0';
      addIfFlag(target + "+r", value, 0b100);
      addIfFlag(target + "+w", value, 0b010);
      addIfFlag(target + "+x", value, 0b001);
    }

    private void addSet(char digit) {
      int value = digit - '0';
      addIfFlag("u+s", value, 0b100);
      addIfFlag("g+s", value, 0b010);
      addIfFlag("+t", value, 0b001);
    }

    private void addIfFlag(String right, int number, int flag) {
      if ((number & flag) > 0) {
        rights.add(right);
      }
    }

    private void addRights(String targets, String rights) {
      if (targets.isEmpty() || targets.equals("a")) {
        targets = "ugo";
      }

      for (char target : targets.toCharArray()) {
        for (char right : rights.toCharArray()) {
          this.rights.add(target + "+" + right);
        }
      }
    }

    public boolean hasRight(String right) {
      return rights.contains(right);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy