    public void shouldHaveErrorMessageAlongWithCustomMessageInOutputOfTemplate() {
        Map<String, Object> data = new HashMap<>();
        data.put("errorMessage", "ERROR MESSAGE");

        String output = view.render(data);
        assertThat(output).contains("$('trans_content').update(\"Sorry, an unexpected error occurred [ERROR MESSAGE]. :( Please check the server logs for more information.\");");
    }


    public void shouldHaveTheGenericMessageInOutputOfTemplateWhenCustomErrorMessageIsNotProvided() {
        String output = view.render(new HashMap<>());
        assertThat(output).contains("$('trans_content').update(\"Sorry, an unexpected error occurred. :( Please check the server logs for more information.\");");
    }

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.


    public TestFreeMarkerView(String template) throws Exception {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setDefaultEncoding(StandardCharsets.UTF_8.name());
        configurer.setTemplateLoaderPath("file:src/main/webapp/WEB-INF/vm");

        Properties settings = new Properties();
        settings.put("recognizeStandardFileExtensions", "true");
        configurer.setFreemarkerSettings(settings);

        setConfiguration(configurer.createConfiguration());
        setServletContext(new MockServletContext());
        setRequestContextAttribute(REQUEST_CONTEXT_ATTRIBUTE);
        setUrl(template);
    }


    public void setLogid(String logid) throws PresentationException {
        synchronized (this) {
            if ("-".equals(logid)) {
                this.logid = "";
            } else if(StringUtils.isNotBlank(logid) && logid.matches("[\\w-]+")) {
                this.logid = SolrTools.escapeSpecialCharacters(logid);
            } else {
                throw new PresentationException("The passed logId " + SolrTools.escapeSpecialCharacters(logid) + " contains illegal characters");
            }
        }
    }


    public void setImageToShow(String imageToShow) {
        synchronized (lock) {
            this.imageToShow = imageToShow;
            if (viewManager != null) {
                viewManager.setDropdownSelected(String.valueOf(imageToShow));
            }
            // Reset LOGID (the LOGID setter is called later by PrettyFaces, so if a value is passed, it will still be set)
            try {
                setLogid("");
            } catch (PresentationException e) {
                //cannot be thrown here
            }
            logger.trace("imageToShow: {}", this.imageToShow);
        }
    }


    public void validate(FacesContext context, UIComponent component, String value) throws ValidatorException {
        if (!validate(value)) {
            FacesMessage msg = new FacesMessage(ViewerResourceBundle.getTranslation("pi_errInvalid", null), "");
            msg.setSeverity(FacesMessage.SEVERITY_ERROR);
            throw new ValidatorException(msg);
        }
    }


    public static boolean validate(String name) {
        if (name == null || name.length() > 10_000) {
            return false;
        }
        Matcher m = PATTERN.matcher(name.toLowerCase());
        return m.find();
    }


    public void validate_shouldMatchCorrectName() throws Exception {
        Assert.assertTrue(NameValidator.validate("John Doe"));
    }


    public void validate_shouldNotMatchInvalidName() throws Exception {
        Assert.assertTrue(NameValidator.validate("John Doe<script />"));
    }


	protected void copyResponse(InputStream is, OutputStream out, byte[] head,
			boolean base64) throws IOException
	{
		if (base64)
		{
			int total = 0;

			try (BufferedInputStream in = new BufferedInputStream(is,
					BUFFER_SIZE))
			{
				ByteArrayOutputStream os = new ByteArrayOutputStream();
			    byte[] buffer = new byte[0xFFFF];

				os.write(head, 0, head.length);

			    for (int len = is.read(buffer); len != -1; len = is.read(buffer))
			    {
					total += len;

					if (total > MAX_FETCH_SIZE)
					{
						throw new IOException("Size limit exceeded");
					}

			        os.write(buffer, 0, len);
			    }

				out.write(mxBase64.encodeToString(os.toByteArray(), false).getBytes());
			}
		}
		else
		{
			out.write(head);
			Utils.copyRestricted(is, out, MAX_FETCH_SIZE);
		}
	}

The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.


	public static void copy(InputStream in, OutputStream out, int bufferSize)
			throws IOException
	{
		copy(in, out, bufferSize, 0);
	}


	public static int copy(InputStream in, OutputStream out, int bufferSize, int sizeLimit)
			throws IOException
	{
		byte[] b = new byte[bufferSize];
		int read, total = 0;

		while ((read = in.read(b)) != -1)
		{
			total += read;

			if (sizeLimit > 0 && total > sizeLimit)
			{
				throw new IOException("Size limit exceeded");
			}

			out.write(b, 0, read);
		}

		return total;
	}


	public static int copyRestricted(InputStream in, OutputStream out, int sizeLimit) throws IOException
	{
		return copy(in, out, IO_BUFFER_SIZE, sizeLimit);
	}


    private String generateTooltipHtml(CmsListInfoBean infoBean) {

        Element root = DOM.createElement("div");
        appendDom(appendDom(root, "p"), "b").setInnerText(CmsClientStringUtil.shortenString(infoBean.getTitle(), 70));
        if (infoBean.hasAdditionalInfo()) {
            for (CmsAdditionalInfoBean additionalInfo : infoBean.getAdditionalInfo()) {
                appendDom(root, "p").setInnerText(
                    additionalInfo.getName()
                        + ":\u00a0"
                        + CmsClientStringUtil.shortenString(additionalInfo.getValue(), 45));
            }
        }
        return root.getInnerHTML();
    }


    private static Element appendDom(Element parent, String name) {

        Element child = DOM.createElement(name);
        parent.appendChild(child);
        return child;
    }


  public DefaultStompHandler(Vertx vertx) {
    this.vertx = vertx;
    this.context = Vertx.currentContext();
    this.destinations = vertx.sharedData().getLocalMap("stomp.destinations");
    this.users = new ConcurrentHashMap<>();
    this.connectHandler = new DefaultConnectHandler();
  }

When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.


  public void setUp(TestContext context) {
    vertx = rule.vertx();
    AuthenticationProvider provider = PropertyFileAuthentication.create(vertx, "test-auth.properties");
    server = StompServer.create(vertx, new StompServerOptions()
        .setSecured(true)
        .setWebsocketBridge(true)
        .setWebsocketPath("/stomp"))
      .handler(StompServerHandler.create(vertx).authProvider(provider));
    server.listen(StompServerOptions.DEFAULT_STOMP_PORT).onComplete(context.asyncAssertSuccess());
    wsServer = vertx.createHttpServer(new HttpServerOptions().setWebSocketSubProtocols(Arrays.asList("v10.stomp", "v11.stomp")))
      .webSocketHandler(server.webSocketHandler());
    wsServer.listen(8080).onComplete(context.asyncAssertSuccess());
    wsClient = vertx.createHttpClient();
    client = StompClient.create(vertx, new StompClientOptions().setLogin("admin").setPasscode("admin"));
  }


  public void testWebSocketClientMustBeConnected(TestContext context) {
    Async async = context.async();
    testClientMustBeConnected(context, v -> {
      wsClient.webSocket(8080, "localhost", "/stomp").onComplete(context.asyncAssertSuccess(ws -> {
        Buffer received = Buffer.buffer();
        ws.binaryMessageHandler(received::appendBuffer);
        ws.writeBinaryMessage(
          Buffer.buffer("SEND\n" +
            "destination:/test\n" +
            "\n" +
            "hello" +
            FrameParser.NULL));
        ws.endHandler(v2 -> {
          context.assertTrue(received.toString().startsWith("ERROR\n"));
          async.complete();
        });
      }));
    });
  }


  public void testTCPClientMustBeConnected(TestContext context) {
    Async async = context.async();
    NetClient client = vertx.createNetClient();
    testClientMustBeConnected(context, v -> {
      client.connect(server.actualPort(), "0.0.0.0").onComplete(context.asyncAssertSuccess(so -> {
        Buffer received = Buffer.buffer();
        so.handler(received::appendBuffer);
        so.write(
          "SEND\n" +
            "destination:/test\n" +
            "\n" +
            "hello" +
            FrameParser.NULL);
        so.endHandler(v2 -> {
          context.assertTrue(received.toString().startsWith("ERROR\n"));
          async.complete();
        });
      }));
    });
  }


  void validate(TestContext context, Buffer buffer) {
    context.assertTrue(buffer.toString().contains("CONNECTED"), "Was expected <" + buffer.toString() + "> to contain 'CONNECTED'");
    context.assertTrue(buffer.toString().contains("version:1.2"));

    User user = server.stompHandler().getUserBySession(extractSession(buffer.toString()));
    context.assertNotNull(user);
  }


  private void testClientMustBeConnected(TestContext context, Handler<Void> cont) {
    client
      .connect(server.actualPort(), "localhost")
      .onComplete(context.asyncAssertSuccess(conn -> {
        Future<String> fut = conn.subscribe("/test", frame -> {
          context.fail("Should not receive a messsage");
        });
        fut.onComplete(context.asyncAssertSuccess(v2 -> {
          cont.handle(null);
        }));
      }));
  }


    public boolean checkUserPassword(String userId, String password) {
        if (StringUtils.isBlank(userId)) {
            MSException.throwException(Translator.get("user_name_is_null"));
        }
        if (StringUtils.isBlank(password)) {
            MSException.throwException(Translator.get("password_is_null"));
        }
        if (userId.length() > 64) {
            MSException.throwException(Translator.get("user_id_length_too_long"));
        }
        if (password.length() > 30) {
            MSException.throwException(Translator.get("password_length_too_long"));
        }
        UserExample example = new UserExample();
        example.createCriteria().andIdEqualTo(userId).andPasswordEqualTo(CodingUtil.md5(password));
        return userMapper.countByExample(example) > 0;
    }

Allocation of Resources Without Limits or Throttling

The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.


    public List<User> getCurrentWorkspaceMember() {
        SessionUser user = SessionUtils.getUser();
        Optional<UserGroup> any = user.getUserGroups().stream()
                .filter(ug -> (ug.getSourceId().equals(SessionUtils.getCurrentWorkspaceId()) || ug.getGroupId().equals(UserGroupConstants.SUPER_GROUP)))
                .findAny();
        if (any.isEmpty()) {
            return new ArrayList<>();
        }
        QueryMemberRequest request = new QueryMemberRequest();
        request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
        return baseUserService.getMemberList(request);
    }

The product does not perform an authorization check when an actor attempts to access a resource or perform an action.


    public List<User> getProjectMembers(@PathVariable String projectId) {
        SessionUser user = SessionUtils.getUser();
        Optional<UserGroup> any = user.getUserGroups().stream()
                .filter(ug -> (ug.getSourceId().equals(projectId) || ug.getGroupId().equals(UserGroupConstants.SUPER_GROUP)))
                .findAny();
        if (any.isEmpty()) {
            return new ArrayList<>();
        }
        QueryMemberRequest request = new QueryMemberRequest();
        request.setProjectId(projectId);
        return baseUserService.getProjectMemberList(request);
    }


    public List<User> getProjectMemberListAll() {
        SessionUser user = SessionUtils.getUser();
        Optional<UserGroup> any = user.getUserGroups().stream()
                .filter(ug -> (ug.getSourceId().equals(SessionUtils.getCurrentProjectId()) || ug.getGroupId().equals(UserGroupConstants.SUPER_GROUP)))
                .findAny();
        if (any.isEmpty()) {
            return new ArrayList<>();
        }
        QueryMemberRequest request = new QueryMemberRequest();
        request.setProjectId(SessionUtils.getCurrentProjectId());
        return baseUserService.getProjectMemberList(request);
    }


    public List<GroupDTO> getAllGroup() {
        SessionUser user = SessionUtils.getUser();
        Optional<UserGroup> any = user.getUserGroups().stream()
                .filter(ug -> (ug.getGroupId().equals(UserGroupConstants.SUPER_GROUP)))
                .findAny();
        if (any.isEmpty()) {
            return new ArrayList<>();
        }
        return groupService.getAllGroup();
    }


        for (char c = 0; c < FILTER_ESCAPE_TABLE.length; c++) {
            FILTER_ESCAPE_TABLE[c] = String.valueOf(c);
        }

The product does not validate, or incorrectly validates, a certificate.


    private String filterEncode(String value)
    {
        if (value == null)
        {
            return null;
        }

        // make buffer roomy
        StringBuilder encodedValue = new StringBuilder(value.length() * 2);

        int length = value.length();

        for (int i = 0; i < length; i++) {

            char c = value.charAt(i);

            if (c < FILTER_ESCAPE_TABLE.length) {
                encodedValue.append(FILTER_ESCAPE_TABLE[c]);
            }
            else {
                // default: add the char
                encodedValue.append(c);
            }
        }

        return encodedValue.toString();
    }


	public <A extends Output<E>, E extends Exception> void append(A a, char c) throws E {
		switch (c) {
			case '"' -> {
				a.append(QUOT);
			}
			case '&' -> {
				a.append(AMP);
			}
			case '\'' -> {
				a.append(APOS);
			}
			case '<' -> {
				a.append(LT);
			}
			case '=' -> {
				a.append(EQUAL);
			}
			case '>' -> {
				a.append(GT);
			}
			case '`' -> {
				a.append(BACK_TICK);
			}
			default -> {
				a.append(c);
			}
		}
	}


	public <A extends Output<E>, E extends Exception> void append(A a, CharSequence csq, int start, int end) throws E {
		csq = csq == null ? "null" : csq;
		for (int i = start; i < end; i++) {
			char c = csq.charAt(i);
			switch (c) {
				case '"' -> { // 34
					a.append(csq, start, i);
					start = i + 1;
					a.append(QUOT);
				}
				case '&' -> { // 38
					a.append(csq, start, i);
					start = i + 1;
					a.append(AMP);

				}
				case '\'' -> { // 39
					a.append(csq, start, i);
					start = i + 1;
					a.append(APOS);
				}
				case '<' -> { // 60
					a.append(csq, start, i);
					start = i + 1;
					a.append(LT);
				}
				case '=' -> { // 61
					a.append(csq, start, i);
					start = i + 1;
					a.append(EQUAL);
				}
				case '>' -> { // 62
					a.append(csq, start, i);
					start = i + 1;
					a.append(GT);
				}
				case '`' -> { // 96
					a.append(csq, start, i);
					start = i + 1;
					a.append(BACK_TICK);
				}
			}
		}
		a.append(csq, start, end);

	}


	public record EscapeModel(String attr, String body) {

	}


	public void testName() throws Exception {
		var m = new EscapeModel("\"&\'<=>`", "\"&\'<=>`");
		String actual = EscapeModelRenderer.of().execute(m);
		String expected = """
				<div alt=""&'<=>`">
				"&'<=>`
				</div>
				""";

		assertEquals(expected, actual);
	}


	public void testParent() throws Exception {
		LambdaSectionParent m = new LambdaSectionParent("ignore");
		String actual = JStachio.render(m);
		String expected = """
				bingo
				LambdaSectionParent[stuff=ignore]
								""";
		assertEquals(expected, actual);
	}


	public AFile getAFile(String nameOrPath) throws IOException {
		final SFile filecurrent;
		// Log.info("AParentFolderRegular::looking for " + nameOrPath);
		// Log.info("AParentFolderRegular::dir = " + dir);
		if (dir == null)
			filecurrent = new SFile(nameOrPath);
		else
			filecurrent = dir.getAbsoluteFile().file(nameOrPath);

		// Log.info("AParentFolderRegular::Filecurrent " + filecurrent);
		if (filecurrent.exists())
			return new AFileRegular(filecurrent.getCanonicalFile());

		return null;
	}

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.


	public UGraphic drawAndMove(UGraphic ug, FontConfiguration fontConfiguration, ISkinParam skinParam, double deltax) {
		final Display display = Display.getWithNewlines(getEmoticon() + getName());
		TextBlock result = display.create(fontConfiguration, HorizontalAlignment.LEFT, skinParam);
		result.drawU(ug.apply(UTranslate.dx(deltax)));
		ug = ug.apply(UTranslate.dy(result.calculateDimension(ug.getStringBounder()).getHeight() + 2));
		for (FilesEntry child : children)
			ug = child.drawAndMove(ug, fontConfiguration, skinParam, deltax + 21);
		return ug;
	}


	private FilesEntry getOrCreateFolder(String folderName) {
		for (FilesEntry child : children)
			if (child.type == FilesType.FOLDER && child.getName().equals(folderName))
				return child;

		final FilesEntry result = new FilesEntry(folderName, FilesType.FOLDER);
		children.add(result);
		return result;
	}


	public FilesEntry(String name, FilesType type) {
		this.name = name;
		this.type = type;
	}


	public String getEmoticon() {
		if (type == FilesType.FOLDER)
			return "<:1f4c2:>";
		// return "<:1f4c1:>";
		return "<:1f4c4:>";
	}


	public FilesEntry addRawEntry(String raw) {
		final int x = raw.indexOf('/');
		if (x == -1) {
			final FilesEntry result = new FilesEntry(raw, FilesType.DATA);
			children.add(result);
			return result;
		}
		final FilesEntry folder = getOrCreateFolder(raw.substring(0, x));
		final String remain = raw.substring(x + 1);
		if (remain.length() == 0)
			return folder;
		return folder.addRawEntry(remain);
	}


	public String getName() {
		return name;
	}


	public Iterator<FilesEntry> iterator() {
		return Collections.unmodifiableCollection(children).iterator();
	}


	public void drawU(UGraphic ug) {
		for (FilesEntry ent : root)
			ug = ent.drawAndMove(ug, fontConfiguration, skinParam, 0);
	}


	public void add(String line) {
		if (line.startsWith("/"))
			root.addRawEntry(line.substring(1));
	}


	public UImage toUImage(ColorMapper colorMapper, HColor backcolor, HColor forecolor) {
		final BufferedImage im = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

		if (backcolor == null)
			backcolor = HColors.WHITE;

		if (forecolor == null)
			forecolor = HColors.BLACK;

		final HColorGradient gradient = HColors.gradient(backcolor, forecolor, '\0');
		for (int col = 0; col < width; col++) {
			for (int line = 0; line < height; line++) {
				final int localColor = color[line][col];
				if (localColor == -1) {
					final double coef = 1.0 * gray[line][col] / (16 - 1);
					final Color c = gradient.getColor(colorMapper, coef, 255);
					im.setRGB(col, line, c.getRGB());
				} else {
					im.setRGB(col, line, localColor);
				}
			}
		}
		return new UImage(new PixelImage(im, AffineTransformType.TYPE_BILINEAR));
	}


	public void setGray(int x, int y, int level) {
		if (x < 0 || x >= width)
			return;

		if (y < 0 || y >= height)
			return;

		if (level < 0 || level >= 16)
			throw new IllegalArgumentException();

		gray[y][x] = level;
		color[y][x] = -1;
	}


	public void setColor(int x, int y, int col) {
		if (x < 0 || x >= width)
			return;

		if (y < 0 || y >= height)
			return;

		gray[y][x] = -1;
		color[y][x] = col;
	}


	private void eventuallyConnectAllStandaloneServersToHiddenNetwork() {
		Network first = null;
		for (NServer server : servers.values())
			if (server.isAlone()) {
				if (first == null) {
					first = createNetwork("");
					first.goInvisible();
				}
				server.connectMeIfAlone(first);
			}
	}


	public CommandExecutionResult openNetwork(String name) {
		if (initDone == false)
			return errorNoInit();

		if (currentGroup() != null)
			return CommandExecutionResult.error("Cannot open network in a group");

		for (NStackable element : stack)
			if (element instanceof Network)
				return CommandExecutionResult.error("Cannot nest network");

		if (networks.size() == 0 && groups.size() == 0)
			eventuallyConnectAllStandaloneServersToHiddenNetwork();

		final Network network = createNetwork(name);
		stack.add(0, network);
		return CommandExecutionResult.ok();
	}


	public void learnThisAddress(String address) {
		if (address == null)
			address = "";
		for (Entry<Network, String> ent : connections.entrySet()) {
			if (ent.getValue().length() == 0) {
				connections.put(ent.getKey(), address);
				return;
			}
		}

	}


	private boolean isInUrlAllowList() {
		final String full = cleanPath(internal.toString());
		// Thanks to Agasthya Kasturi
		if (full.contains("@"))
			return false;
		for (String allow : getUrlAllowList())
			if (full.startsWith(cleanPath(allow)))
				return true;

		return false;
	}


	private boolean forbiddenURL(String full) {
		// Thanks to Agasthya Kasturi
		if (full.contains("@"))
			return true;
		if (full.startsWith("https://") == false && full.startsWith("http://") == false)
			return true;
		if (full.matches("^https?://[-#.0-9:\\[\\]+]+/.*"))
			return true;
		if (full.matches("^https?://[^.]+/.*"))
			return true;
		if (full.matches("^https?://[^.]+$"))
			return true;
		return false;
	}


	private TValue executeReturnLegacyDefine(LineLocation location, TContext context, TMemory memory, List<TValue> args)
			throws EaterException, EaterExceptionLocated {
		if (legacyDefinition == null)
			throw new IllegalStateException();

		final TMemory copy = getNewMemory(memory, args, Collections.<String, TValue>emptyMap());
		final String tmp = context.applyFunctionsAndVariables(copy, location, legacyDefinition);
		if (tmp == null)
			return TValue.fromString("");

		return TValue.fromString(tmp);
		// eaterReturn.execute(context, copy);
		// // System.err.println("s3=" + eaterReturn.getValue2());
		// return eaterReturn.getValue2();
	}


	public void executeProcedureInternal(TContext context, TMemory memory, List<TValue> args, Map<String, TValue> named)
			throws EaterException, EaterExceptionLocated {
		if (functionType != TFunctionType.PROCEDURE && functionType != TFunctionType.LEGACY_DEFINELONG)
			throw new IllegalStateException();

		final TMemory copy = getNewMemory(memory, args, named);
		context.executeLines(copy, body, TFunctionType.PROCEDURE, false);
	}


	public TFunctionImpl(String functionName, List<TFunctionArgument> args, boolean unquoted,
			TFunctionType functionType) {
		final Set<String> names = new HashSet<>();
		for (TFunctionArgument tmp : args)
			names.add(tmp.getName());

		this.signature = new TFunctionSignature(functionName, args.size(), names);
		this.args = args;
		this.unquoted = unquoted;
		this.functionType = functionType;
	}


	public boolean canCover(int nbArg, Set<String> namedArguments) {
		for (String n : namedArguments)
			if (signature.getNamedArguments().contains(n) == false)
				return false;

		if (nbArg > args.size())
			return false;

		assert nbArg <= args.size();
		int neededArgument = 0;
		for (TFunctionArgument arg : args) {
			if (namedArguments.contains(arg.getName()))
				continue;

			if (arg.getOptionalDefaultValue() == null)
				neededArgument++;

		}
		if (nbArg < neededArgument)
			return false;

		assert nbArg >= neededArgument;
		return true;
	}


	public void addBody(StringLocated s) throws EaterExceptionLocated {
		body.add(s);
		if (s.getType() == TLineType.RETURN) {
			this.containsReturn = true;
			if (functionType == TFunctionType.PROCEDURE)
				throw EaterExceptionLocated
						.located("A procedure cannot have !return directive. Declare it as a function instead ?", s);
		}
	}


	public void finalizeEnddefinelong() {
		if (functionType != TFunctionType.LEGACY_DEFINELONG)
			throw new UnsupportedOperationException();

		if (body.size() == 1) {
			this.functionType = TFunctionType.LEGACY_DEFINE;
			this.legacyDefinition = body.get(0).getString();
		}
	}


	public TValue executeReturnFunction(TContext context, TMemory memory, LineLocation location, List<TValue> args,
			Map<String, TValue> named) throws EaterException, EaterExceptionLocated {
		if (functionType == TFunctionType.LEGACY_DEFINE)
			return executeReturnLegacyDefine(location, context, memory, args);

		if (functionType != TFunctionType.RETURN_FUNCTION)
			throw EaterException.unlocated("Illegal call here. Is there a return directive in your function?");

		final TMemory copy = getNewMemory(memory, args, named);
		final TValue result = context.executeLines(copy, body, TFunctionType.RETURN_FUNCTION, true);
		if (result == null)
			throw EaterException.unlocated("No return directive found in your function");

		return result;
	}


	private TMemory getNewMemory(TMemory memory, List<TValue> values, Map<String, TValue> namedArguments) {
		final Map<String, TValue> result = new HashMap<String, TValue>();
		int ivalue = 0;
		for (TFunctionArgument arg : args) {
			final TValue value;
			if (namedArguments.containsKey(arg.getName())) {
				value = namedArguments.get(arg.getName());
			} else if (ivalue < values.size()) {
				value = values.get(ivalue);
				ivalue++;
			} else {
				value = arg.getOptionalDefaultValue();
			}
			if (value == null)
				throw new IllegalStateException();

			result.put(arg.getName(), value);
		}
		return memory.forkFromGlobal(result);
	}


	public static Collection<SFile> fileCandidates() {
		final Set<SFile> result = new TreeSet<>();
		final String classpath = System.getProperty("java.class.path");
		String[] classpathEntries = classpath.split(SFile.pathSeparator);
		for (String s : classpathEntries) {
			if (s == null)
				continue;
			SFile dir = new SFile(s);
			if (dir.isFile())
				dir = dir.getParentFile();

			if (dir != null && dir.isDirectory())
				result.add(dir.file("license.txt"));

		}
		return result;
	}


	public static BufferedImage retrieveDistributorImage(LicenseInfo licenseInfo) {
		if (licenseInfo.getLicenseType() != LicenseType.DISTRIBUTOR) {
			return null;
		}
		try {
			final byte[] s1 = PLSSignature.retrieveDistributorImageSignature();
			if (SignatureUtils.toHexString(s1).equals(SignatureUtils.toHexString(licenseInfo.sha)) == false)
				return null;

			final InputStream dis = PSystemVersion.class.getResourceAsStream("/distributor.png");
			if (dis == null)
				return null;

			try {
				final BufferedImage result = SImageIO.read(dis);
				return result;
			} finally {
				dis.close();
			}
		} catch (Exception e) {
			Logme.error(e);
		}
		return null;
	}


	public static synchronized LicenseInfo retrieveNamedSlow() {
		cache = LicenseInfo.NONE;
		if (OptionFlags.ALLOW_INCLUDE == false)
			return cache;

		final String key = prefs.get("license", "");
		if (key.length() > 0) {
			cache = setIfValid(retrieveNamed(key), cache);
			if (cache.isValid())
				return cache;

		}
		for (SFile f : fileCandidates()) {
			try {
				if (f.exists() && f.canRead()) {
					final LicenseInfo result = retrieve(f);
					if (result == null)
						return null;

					cache = setIfValid(result, cache);
					if (cache.isValid())
						return cache;

				}
			} catch (IOException e) {
				Log.info("Error " + e);
				// Logme.error(e);
			}
		}
		return cache;
	}


	private static LicenseInfo retrieve(SFile f) throws IOException {
		final BufferedReader br = f.openBufferedReader();
		if (br == null)
			return null;

		try {
			final String s = br.readLine();
			final LicenseInfo result = retrieveNamed(s);
			if (result != null)
				Log.info("Reading license from " + f.getAbsolutePath());

			return result;
		} finally {
			br.close();
		}
	}


	public static LicenseInfo retrieveDistributor() {
		final InputStream dis = PSystemVersion.class.getResourceAsStream("/distributor.txt");
		if (dis == null)
			return null;

		try {
			final BufferedReader br = new BufferedReader(new InputStreamReader(dis));
			final String licenseString = br.readLine();
			br.close();
			final LicenseInfo result = PLSSignature.retrieveDistributor(licenseString);
			final Throwable creationPoint = new Throwable();
			creationPoint.fillInStackTrace();
			for (StackTraceElement ste : creationPoint.getStackTrace())
				if (ste.toString().contains(result.context))
					return result;

			return null;
		} catch (Exception e) {
			Logme.error(e);
			return null;
		}
	}


	private static LicenseInfo setIfValid(LicenseInfo value, LicenseInfo def) {
		if (value.isValid() || def.isNone())
			return value;

		return def;
	}


	public AFile getAFile(String nameOrPath) throws IOException {
		// Log.info("ImportedFiles::getAFile nameOrPath = " + nameOrPath);
		// Log.info("ImportedFiles::getAFile currentDir = " + currentDir);
		final AParentFolder dir = currentDir;
		if (dir == null || isAbsolute(nameOrPath))
			return new AFileRegular(new SFile(nameOrPath).getCanonicalFile());

		// final File filecurrent = SecurityUtils.File(dir.getAbsoluteFile(),
		// nameOrPath);
		final AFile filecurrent = dir.getAFile(nameOrPath);
		Log.info("ImportedFiles::getAFile filecurrent = " + filecurrent);
		if (filecurrent != null && filecurrent.isOk())
			return filecurrent;

		for (SFile d : getPath()) {
			if (d.isDirectory()) {
				final SFile file = d.file(nameOrPath);
				if (file.exists())
					return new AFileRegular(file.getCanonicalFile());

			} else if (d.isFile()) {
				final AFileZipEntry zipEntry = new AFileZipEntry(d, nameOrPath);
				if (zipEntry.isOk())
					return zipEntry;

			}
		}
		return filecurrent;
	}

The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.


	public ImportedFiles withCurrentDir(AParentFolder newCurrentDir) {
		if (newCurrentDir == null)
			return this;

		return new ImportedFiles(imported, newCurrentDir);
	}


	public FileWithSuffix getFile(String filename, String suffix) throws IOException {
		final int idx = filename.indexOf('~');
		final AFile file;
		final String entry;
		if (idx == -1) {
			file = getAFile(filename);
			entry = null;
		} else {
			file = getAFile(filename.substring(0, idx));
			entry = filename.substring(idx + 1);
		}
		// if (isAllowed(file) == false)
		if (file == null || file.getUnderlyingFile().isFileOk() == false)
			return FileWithSuffix.none();

		return new FileWithSuffix(filename, suffix, file, entry);
	}


	public boolean isFileOk() {
		// ::comment when __CORE__
		if (SecurityUtils.getSecurityProfile() == SecurityProfile.SANDBOX)
			// In SANDBOX, we cannot read any files
			return false;

		// In any case SFile should not access the security folders
		// (the files must be handled internally)
		try {
			if (isDenied())
				return false;
		} catch (IOException e) {
			return false;
		}
		// Files in "plantuml.include.path" and "plantuml.allowlist.path" are ok.
		if (isInAllowList(SecurityUtils.getPath(SecurityUtils.PATHS_INCLUDES)))
			return true;

		if (isInAllowList(SecurityUtils.getPath(SecurityUtils.ALLOWLIST_LOCAL_PATHS)))
			return true;

		if (SecurityUtils.getSecurityProfile() == SecurityProfile.INTERNET)
			return false;

		if (SecurityUtils.getSecurityProfile() == SecurityProfile.ALLOWLIST)
			return false;

		if (SecurityUtils.getSecurityProfile() != SecurityProfile.UNSECURE) {
			// For UNSECURE, we did not do those checks
			final String path = getCleanPathSecure();
			if (path.startsWith("/etc/") || path.startsWith("/dev/") || path.startsWith("/boot/")
					|| path.startsWith("/proc/") || path.startsWith("/sys/"))
				return false;

			if (path.startsWith("//"))
				return false;

		}
		// ::done
		return true;
	}


	public static SFile fromFile(File internal) {
		if (internal == null)
			return null;

		return new SFile(internal);
	}


	public boolean isUrlOk() {
		// ::comment when __CORE__
		if (SecurityUtils.getSecurityProfile() == SecurityProfile.SANDBOX)
			// In SANDBOX, we cannot read any URL
			return false;

		if (SecurityUtils.getSecurityProfile() == SecurityProfile.LEGACY)
			return true;

		if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE)
			// We are UNSECURE anyway
			return true;

		if (isInUrlAllowList())
			// ::done
			return true;
		// ::comment when __CORE__

		if (SecurityUtils.getSecurityProfile() == SecurityProfile.INTERNET) {
			if (forbiddenURL(cleanPath(internal.toString())))
				return false;

			final int port = internal.getPort();
			// Using INTERNET profile, port 80 and 443 are ok
			return port == 80 || port == 443 || port == -1;
		}
		return false;
		// ::done
	}


	public boolean canWeReadThisEnvironmentVariable(String name) {
		if (name == null)
			return false;

		if (this == UNSECURE)
			return true;

		if (name.toLowerCase().startsWith("plantuml"))
			return true;

		return true;
	}


	public TValue executeReturnFunction(TContext context, TMemory memory, LineLocation location, List<TValue> values,
			Map<String, TValue> named) throws EaterException, EaterExceptionLocated {
		// ::comment when __CORE__
		final String path = values.get(0).toString();
		return TValue.fromBoolean(new SFile(path).exists());
		// ::done

		// ::uncomment when __CORE__
		// return TValue.fromBoolean(false);
		// ::done
	}


	private String getenv(String name) {
		// Check, if the script requests secret information.
		// A plantuml server should have an own SecurityManager to
		// avoid access to properties and environment variables, but we should
		// also stop here in other deployments.
		if (SecurityUtils.getSecurityProfile().canWeReadThisEnvironmentVariable(name) == false)
			return null;

		final String env = System.getProperty(name);
		if (env != null)
			return env;

		return System.getenv(name);
	}


	public TValue executeReturnFunction(TContext context, TMemory memory, LineLocation location, List<TValue> values,
			Map<String, TValue> named) throws EaterException, EaterExceptionLocated {
		// ::comment when __CORE__
		final String value = getenv(values.get(0).toString());
		if (value == null)
			return TValue.fromString("");

		return TValue.fromString(value);
		// ::done

		// ::uncomment when __CORE__
		// return TValue.fromString("");
		// ::done
	}


	private String loadStringData(String path, String charset) throws EaterException, UnsupportedEncodingException {

		byte[] byteData = null;
		if (path.startsWith("http://") || path.startsWith("https://")) {
			final SURL url = SURL.create(path);
			if (url != null)
				byteData = url.getBytes();
			// ::comment when __CORE__
		} else {
			try {
				final SFile file = FileSystem.getInstance().getFile(path);
				if (file != null && file.exists() && file.canRead() && !file.isDirectory()) {
					final ByteArrayOutputStream out = new ByteArrayOutputStream(1024 * 8);
					FileUtils.copyToStream(file, out);
					byteData = out.toByteArray();
				}
			} catch (IOException e) {
				Logme.error(e);
			}
			// ::done
		}

		if (byteData == null || byteData.length == 0)
			return null; // no length, no data (we want the default)

		return new String(byteData, charset);

	}


	public static synchronized LicenseInfo retrieveNamedSlow() {
		cache = LicenseInfo.NONE;
//		if (OptionFlags.ALLOW_INCLUDE == false)
//			return cache;

		final String key = prefs.get("license", "");
		if (key.length() > 0) {
			cache = setIfValid(retrieveNamed(key), cache);
			if (cache.isValid())
				return cache;

		}
		for (SFile f : fileCandidates()) {
			try {
				if (f.exists() && f.canRead()) {
					final LicenseInfo result = retrieve(f);
					if (result == null)
						return null;

					cache = setIfValid(result, cache);
					if (cache.isValid())
						return cache;

				}
			} catch (IOException e) {
				Log.info("Error " + e);
				// Logme.error(e);
			}
		}
		return cache;
	}


	public static PSystemVersion createShowVersion2(UmlSource source) {
		final List<String> strings = new ArrayList<>();
		strings.add("<b>PlantUML version " + Version.versionString() + "</b> (" + Version.compileTimeString() + ")");
		strings.add("(" + License.getCurrent() + " source distribution)");
		// :: uncomment when __CORE__
//		strings.add(" ");
//		strings.add("Compiled with CheerpJ 2.3");
//		strings.add("Powered by CheerpJ, a Leaning Technologies Java tool");
		// :: done
		// :: comment when __CORE__
		GraphvizCrash.checkOldVersionWarning(strings);
		if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) {
			strings.add("Loaded from " + Version.getJarPath());

			if (OptionFlags.getInstance().isWord()) {
				strings.add("Word Mode");
				strings.add("Command Line: " + Run.getCommandLine());
				strings.add("Current Dir: " + new SFile(".").getAbsolutePath());
				strings.add("plantuml.include.path: " + PreprocessorUtils.getenv(SecurityUtils.PATHS_INCLUDES));
			}
		}
		strings.add(" ");

		GraphvizUtils.addDotStatus(strings, true);
		strings.add(" ");
		for (String name : OptionPrint.interestingProperties())
			strings.add(name);

		for (String v : OptionPrint.interestingValues())
			strings.add(v);

		// ::done

		return new PSystemVersion(source, true, strings);
	}


    public static byte[] shuffle(double[] input) throws IOException {
        if (input.length * 8 < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        byte[] output = new byte[input.length * 8];
        int numProcessed = impl.shuffle(input, 0, 8, input.length * 8, output, 0);
        assert(numProcessed == input.length * 8);
        return output;
    }

The product performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can introduce other weaknesses when the calculation is used for resource management or execution control.


    public static byte[] shuffle(float[] input) throws IOException {
        if (input.length * 4 < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        byte[] output = new byte[input.length * 4];
        int numProcessed = impl.shuffle(input, 0, 4, input.length * 4, output, 0);
        assert(numProcessed == input.length * 4);
        return output;
    }


    public static byte[] shuffle(int[] input) throws IOException {
        if (input.length * 4 < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        byte[] output = new byte[input.length * 4];
        int numProcessed = impl.shuffle(input, 0, 4, input.length * 4, output, 0);
        assert(numProcessed == input.length * 4);
        return output;
    }


    public static byte[] shuffle(long[] input) throws IOException {
        if (input.length * 8 < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        byte[] output = new byte[input.length * 8];
        int numProcessed = impl.shuffle(input, 0, 8, input.length * 8, output, 0);
        assert(numProcessed == input.length * 8);
        return output;
    }


    public static byte[] shuffle(short[] input) throws IOException {
        if (input.length * 2 < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        byte[] output = new byte[input.length * 2];
        int numProcessed = impl.shuffle(input, 0, 2, input.length * 2, output, 0);
        assert(numProcessed == input.length * 2);
        return output;
    }


    public void isTooLargeIntArrayInputLengthForBitShuffleShuffle() throws Exception {
        BitShuffle.shuffle(new float[Integer.MAX_VALUE / 4 + 1]);
    }


    public void isValidArrayInputLengthForBitShuffleShuffle()
            throws Exception
    {
        byte[] b = BitShuffle.shuffle(new double[0]);
        byte[] c = BitShuffle.shuffle(new float[0]);
        byte[] d = BitShuffle.shuffle(new int[0]);
        byte[] e = BitShuffle.shuffle(new long[0]);
        byte[] f = BitShuffle.shuffle(new short[0]);
        byte[] n = BitShuffle.shuffle(new double[10]);
        byte[] o = BitShuffle.shuffle(new float[10]);
        byte[] p = BitShuffle.shuffle(new int[10]);
        byte[] q = BitShuffle.shuffle(new long[10]);
        byte[] r = BitShuffle.shuffle(new short[10]);
    }


    public void isTooLargeDoubleArrayInputLengthForBitShuffleShuffle() throws Exception {
        BitShuffle.shuffle(new double[Integer.MAX_VALUE / 8 + 1]);
    }


    public void isTooLargeLongArrayInputLengthForBitShuffleShuffle() throws Exception {
        BitShuffle.shuffle(new long[Integer.MAX_VALUE / 8 + 1]);
    }


    public void isTooLargeFloatArrayInputLengthForBitShuffleShuffle() throws Exception {
        BitShuffle.shuffle(new float[Integer.MAX_VALUE / 4 + 1]);
    }


    public void isTooLargeShortArrayInputLengthForBitShuffleShuffle() throws Exception {
        BitShuffle.shuffle(new short[Integer.MAX_VALUE / 2 + 1]);
    }


    public void isTooLargeShortArrayInputLengthForBitShuffleShuffle() throws Exception {
        BitShuffle.shuffle(new short[Integer.MAX_VALUE / 2 + 1]);

    }


    public void isValidChunkLengthForSnappyInputStreamIn()
            throws Exception {
        byte[] data = {0};
        SnappyInputStream in = new SnappyInputStream(new ByteArrayInputStream(data));
        byte[] out = new byte[50];
        in.read(out);
    }


    public void isInvalidChunkLengthForSnappyInputStreamInNegative()
            throws Exception {
        byte[] data = {-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0, 0, 0, 0, 0, 0, 0, 0, 0,(byte) 0x7f, (byte) 0xff, (byte) 0xff, (byte) 0xff};
        SnappyInputStream in = new SnappyInputStream(new ByteArrayInputStream(data));
        byte[] out = new byte[50];
        in.read(out);
    }


    public void isInvalidChunkLengthForSnappyInputStreamOutOfMemory()
            throws Exception {
        byte[] data = {-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0x7f, (byte) 0xff, (byte) 0xff, (byte) 0xff};
        SnappyInputStream in = new SnappyInputStream(new ByteArrayInputStream(data));
        byte[] out = new byte[50];
        try {
            in.read(out);
        } catch (Exception ignored) {
            // Exception here will be catched
            // But OutOfMemoryError will not be caught, and will still be thrown
        }
    }


    public static byte[] compress(int[] input)
            throws IOException
    {
        int byteSize = input.length * 4;
        if (byteSize < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        return rawCompress(input, byteSize); // int uses 4 bytes
    }


    public static byte[] compress(double[] input)
            throws IOException
    {
        int byteSize = input.length * 8;
        if (byteSize < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        return rawCompress(input, byteSize); // double uses 8 bytes
    }


    public static byte[] compress(float[] input)
            throws IOException
    {
        int byteSize = input.length * 4;
        if (byteSize < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        return rawCompress(input, byteSize); // float uses 4 bytes
    }


    public static byte[] compress(short[] input)
            throws IOException
    {
        int byteSize = input.length * 2;
        if (byteSize < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        return rawCompress(input, byteSize); // short uses 2 bytes
    }


    public static byte[] compress(char[] input)
            throws IOException
    {
        int byteSize = input.length * 2;
        if (byteSize < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        return rawCompress(input, byteSize); // char uses 2 bytes
    }


    public static byte[] compress(long[] input)
            throws IOException
    {
        int byteSize = input.length * 8;
        if (byteSize < input.length) {
            throw new SnappyError(SnappyErrorCode.TOO_LARGE_INPUT, "input array size is too large: " + input.length);
        }
        return rawCompress(input, byteSize); // long uses 8 bytes
    }


    public void isTooLargeCharArrayInputLength() throws Exception {
        Snappy.compress(new char[Integer.MAX_VALUE / 2 + 1]);
    }


    public void isTooLargeFloatArrayInputLength() throws Exception {
        Snappy.compress(new float[Integer.MAX_VALUE / 4 + 1]);
    }


    public void isTooLargeIntArrayInputLength() throws Exception {
        Snappy.compress(new int[Integer.MAX_VALUE / 4 + 1]);
    }


    public void isTooLargeDoubleArrayInputLength() throws Exception {
        Snappy.compress(new double[Integer.MAX_VALUE / 8 + 1]);
    }


    public void isValidArrayInputLength()
            throws Exception {
        byte[] a = Snappy.compress(new char[0]);
        byte[] b = Snappy.compress(new double[0]);
        byte[] c = Snappy.compress(new float[0]);
        byte[] d = Snappy.compress(new int[0]);
        byte[] e = Snappy.compress(new long[0]);
        byte[] f = Snappy.compress(new short[0]);
        byte[] g = Snappy.compress(new char[10]);
        byte[] h = Snappy.compress(new double[10]);
        byte[] i = Snappy.compress(new float[10]);
        byte[] j = Snappy.compress(new int[10]);
        byte[] k = Snappy.compress(new long[10]);
        byte[] l = Snappy.compress(new short[10]);
    }


    public void isTooLargeLongArrayInputLength() throws Exception {
        Snappy.compress(new long[Integer.MAX_VALUE / 8 + 1]);
    }


    public void isTooLargeShortArrayInputLength() throws Exception {
        Snappy.compress(new short[Integer.MAX_VALUE / 2 + 1]);
    }


    public SnappyInputStream(InputStream input)
            throws IOException
    {
        this(input, MAX_CHUNK_SIZE);
    }


    public SnappyInputStream(InputStream input, int maxChunkSize)
            throws IOException
    {
        this.maxChunkSize = maxChunkSize;
        this.in = input;
        readHeader();
    }


    public SnappyOutputStream(OutputStream out, int blockSize, BufferAllocatorFactory bufferAllocatorFactory)
    {
        this.out = out;
        this.blockSize = Math.max(MIN_BLOCK_SIZE, blockSize);
        if (this.blockSize > MAX_BLOCK_SIZE){
            throw new IllegalArgumentException(String.format("Provided chunk size %,d larger than max %,d", this.blockSize, MAX_BLOCK_SIZE));
        }
        int inputSize = blockSize;
        int outputSize = SnappyCodec.HEADER_SIZE + 4 + Snappy.maxCompressedLength(blockSize);

        this.inputBufferAllocator = bufferAllocatorFactory.getBufferAllocator(inputSize);
        this.outputBufferAllocator = bufferAllocatorFactory.getBufferAllocator(outputSize);

        inputBuffer = inputBufferAllocator.allocate(inputSize);
        outputBuffer = outputBufferAllocator.allocate(outputSize);
    }


    public void invalidBlockSize()
            throws Exception
    {
        // We rely on catch below, if there is no error this test will pass
        // This can be done better with Assertions.assertThrows
        Boolean exceptionThrown = false;
        ByteArrayOutputStream b = new ByteArrayOutputStream();
        SnappyOutputStream os = new SnappyOutputStream(b, 1024 * 1024 * 1024);
    }


    public void isInvalidChunkLengthForSnappyInputStream()
            throws Exception {
        byte[] data = {-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0x7f, (byte) 0xff, (byte) 0xff, (byte) 0xff};
        SnappyInputStream in = new SnappyInputStream(new ByteArrayInputStream(data));
        byte[] out = new byte[50];
        try {
            in.read(out);
        } catch (SnappyError error) {
            Assert.assertEquals(error.errorCode, SnappyErrorCode.FAILED_TO_UNCOMPRESS);
        }
    }


    public AbstractSniHandler() {
        this(0, 0L);
    }


    protected AbstractSniHandler(int maxClientHelloLength, long handshakeTimeoutMillis) {
        super(maxClientHelloLength);
        this.handshakeTimeoutMillis = checkPositiveOrZero(handshakeTimeoutMillis, "handshakeTimeoutMillis");
    }


    protected AbstractSniHandler(long handshakeTimeoutMillis) {
        this(0, handshakeTimeoutMillis);
    }


    public SniHandler(Mapping<? super String, ? extends SslContext> mapping,
                      int maxClientHelloLength, long handshakeTimeoutMillis) {
        this(new AsyncMappingAdapter(mapping), maxClientHelloLength, handshakeTimeoutMillis);
    }


    public SniHandler(AsyncMapping<? super String, ? extends SslContext> mapping) {
        this(mapping, 0, 0L);
    }


    public SniHandler(AsyncMapping<? super String, ? extends SslContext> mapping,
                      int maxClientHelloLength, long handshakeTimeoutMillis) {
        super(maxClientHelloLength, handshakeTimeoutMillis);
        this.mapping = (AsyncMapping<String, SslContext>) ObjectUtil.checkNotNull(mapping, "mapping");
    }


    public SniHandler(Mapping<? super String, ? extends SslContext> mapping, long handshakeTimeoutMillis) {
        this(new AsyncMappingAdapter(mapping), handshakeTimeoutMillis);
    }


    public SniHandler(AsyncMapping<? super String, ? extends SslContext> mapping, long handshakeTimeoutMillis) {
        this(mapping, 0, handshakeTimeoutMillis);
    }


    protected SslClientHelloHandler(int maxClientHelloLength) {
        // 16MB is the maximum as per RFC:
        // See https://www.rfc-editor.org/rfc/rfc5246#section-6.2.1
        this.maxClientHelloLength =
                ObjectUtil.checkInRange(maxClientHelloLength, 0, MAX_CLIENT_HELLO_LENGTH, "maxClientHelloLength");
    }


    public SslClientHelloHandler() {
        this(MAX_CLIENT_HELLO_LENGTH);
    }


    public void testSniHandlerFiresHandshakeTimeout() throws Exception {
        SniHandler handler = new SniHandler(new Mapping<String, SslContext>() {
            @Override
            public SslContext map(String input) {
                throw new UnsupportedOperationException("Should not be called");
            }
        }, 0, 10);

        final AtomicReference<SniCompletionEvent> completionEventRef =
            new AtomicReference<SniCompletionEvent>();
        EmbeddedChannel ch = new EmbeddedChannel(handler, new ChannelInboundHandlerAdapter() {
            @Override
            public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
                if (evt instanceof SniCompletionEvent) {
                    completionEventRef.set((SniCompletionEvent) evt);
                }
            }
        });
        try {
            while (completionEventRef.get() == null) {
                Thread.sleep(100);
                // We need to run all pending tasks as the handshake timeout is scheduled on the EventLoop.
                ch.runPendingTasks();
            }
            SniCompletionEvent completionEvent = completionEventRef.get();
            assertNotNull(completionEvent);
            assertNotNull(completionEvent.cause());
            assertEquals(SslHandshakeTimeoutException.class, completionEvent.cause().getClass());
        } finally {
            ch.finishAndReleaseAll();
        }
    }


    protected B decoderEnforceMaxRstFramesPerWindow(int maxRstFramesPerWindow, int secondsPerWindow) {
        enforceNonCodecConstraints("decoderEnforceMaxRstFramesPerWindow");
        this.maxRstFramesPerWindow = checkPositiveOrZero(
                maxRstFramesPerWindow, "maxRstFramesPerWindow");
        this.secondsPerWindow = checkPositiveOrZero(secondsPerWindow, "secondsPerWindow");
        return self();
    }


    private T buildFromCodec(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder) {
        int maxConsecutiveEmptyDataFrames = decoderEnforceMaxConsecutiveEmptyDataFrames();
        if (maxConsecutiveEmptyDataFrames > 0) {
            decoder = new Http2EmptyDataFrameConnectionDecoder(decoder, maxConsecutiveEmptyDataFrames);
        }
        if (maxRstFramesPerWindow > 0 && secondsPerWindow > 0) {
            decoder = new Http2MaxRstFrameDecoder(decoder, maxRstFramesPerWindow, secondsPerWindow);
        }
        final T handler;
        try {
            // Call the abstract build method
            handler = build(decoder, encoder, initialSettings);
        } catch (Throwable t) {
            encoder.close();
            decoder.close();
            throw new IllegalStateException("failed to build an Http2ConnectionHandler", t);
        }

        // Setup post build options
        handler.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);
        if (handler.decoder().frameListener() == null) {
            handler.decoder().frameListener(frameListener);
        }
        return handler;
    }


    public Http2FrameCodecBuilder decoderEnforceMaxRstFramesPerWindow(
            int maxConsecutiveEmptyFrames, int secondsPerWindow) {
        return super.decoderEnforceMaxRstFramesPerWindow(maxConsecutiveEmptyFrames, secondsPerWindow);
    }


    Http2FrameListener frameListener0() {
        return super.frameListener();
    }


    Http2MaxRstFrameDecoder(Http2ConnectionDecoder delegate, int maxRstFramesPerWindow, int secondsPerWindow) {
        super(delegate);
        this.maxRstFramesPerWindow = checkPositive(maxRstFramesPerWindow, "maxRstFramesPerWindow");
        this.secondsPerWindow = checkPositive(secondsPerWindow, "secondsPerWindow");
    }


    public Http2FrameListener frameListener() {
        Http2FrameListener frameListener = frameListener0();
        // Unwrap the original Http2FrameListener as we add this decoder under the hood.
        if (frameListener instanceof Http2MaxRstFrameListener) {
            return ((Http2MaxRstFrameListener) frameListener).listener;
        }
        return frameListener;
    }


    public void frameListener(Http2FrameListener listener) {
        if (listener != null) {
            super.frameListener(new Http2MaxRstFrameListener(listener, maxRstFramesPerWindow, secondsPerWindow));
        } else {
            super.frameListener(null);
        }
    }


    Http2MaxRstFrameListener(Http2FrameListener listener, int maxRstFramesPerWindow, int secondsPerWindow) {
        super(listener);
        this.maxRstFramesPerWindow = maxRstFramesPerWindow;
        this.nanosPerWindow = TimeUnit.SECONDS.toNanos(secondsPerWindow);
    }


    public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
        long currentNano = System.nanoTime();
        if (currentNano - lastRstFrameNano >= nanosPerWindow) {
            lastRstFrameNano = currentNano;
            receivedRstInWindow = 1;
        } else {
            receivedRstInWindow++;
            if (receivedRstInWindow > maxRstFramesPerWindow) {
                Http2Exception exception = Http2Exception.connectionError(Http2Error.ENHANCE_YOUR_CALM,
                        "Maximum number of RST frames reached");
                logger.debug("{} Maximum number {} of RST frames reached within {} seconds, " +
                                "closing connection with {} error", ctx.channel(), maxRstFramesPerWindow,
                        TimeUnit.NANOSECONDS.toSeconds(nanosPerWindow), exception.error(), exception);
                throw exception;
            }
        }
        super.onRstStreamRead(ctx, streamId, errorCode);
    }


    public Http2MultiplexCodecBuilder decoderEnforceMaxRstFramesPerWindow(
            int maxConsecutiveEmptyFrames, int secondsPerWindow) {
        return super.decoderEnforceMaxRstFramesPerWindow(maxConsecutiveEmptyFrames, secondsPerWindow);
    }


    public void testDecoration() {
        Http2ConnectionDecoder delegate = mock(Http2ConnectionDecoder.class);
        final ArgumentCaptor<Http2FrameListener> listenerArgumentCaptor =
                ArgumentCaptor.forClass(Http2FrameListener.class);
        when(delegate.frameListener()).then(new Answer<Http2FrameListener>() {
            @Override
            public Http2FrameListener answer(InvocationOnMock invocationOnMock) {
                return listenerArgumentCaptor.getValue();
            }
        });
        Http2FrameListener listener = mock(Http2FrameListener.class);
        DecoratingHttp2ConnectionDecoder decoder = newDecoder(delegate);
        decoder.frameListener(listener);
        verify(delegate).frameListener(listenerArgumentCaptor.capture());

        assertThat(decoder.frameListener(),
                CoreMatchers.not(CoreMatchers.instanceOf(delegatingFrameListenerType())));
    }


    public void testDecorationWithNull() {
        Http2ConnectionDecoder delegate = mock(Http2ConnectionDecoder.class);

        DecoratingHttp2ConnectionDecoder decoder = newDecoder(delegate);
        decoder.frameListener(null);
        assertNull(decoder.frameListener());
    }


    protected DecoratingHttp2ConnectionDecoder newDecoder(Http2ConnectionDecoder decoder) {
        return new Http2EmptyDataFrameConnectionDecoder(decoder, 2);
    }


    protected Class<? extends Http2FrameListener> delegatingFrameListenerType() {
        return Http2EmptyDataFrameListener.class;
    }


    protected Class<? extends Http2FrameListener> delegatingFrameListenerType() {
        return Http2MaxRstFrameListener.class;
    }


    protected DecoratingHttp2ConnectionDecoder newDecoder(Http2ConnectionDecoder decoder) {
        return new Http2MaxRstFrameDecoder(decoder, 200, 30);
    }


    public void testMaxRstFramesReached() throws Http2Exception {
        listener = new Http2MaxRstFrameListener(frameListener, 1, 10);
        listener.onRstStreamRead(ctx, 1, Http2Error.STREAM_CLOSED.code());

        Http2Exception ex = assertThrows(Http2Exception.class, new Executable() {
            @Override
            public void execute() throws Throwable {
                listener.onRstStreamRead(ctx, 2, Http2Error.STREAM_CLOSED.code());
            }
        });
        assertEquals(Http2Error.ENHANCE_YOUR_CALM, ex.error());
        verify(frameListener, times(1)).onRstStreamRead(eq(ctx), anyInt(), eq(Http2Error.STREAM_CLOSED.code()));
    }


    public void setUp() {
        initMocks(this);
    }


    public void testRstFrames() throws Exception {
        listener = new Http2MaxRstFrameListener(frameListener, 1, 1);
        listener.onRstStreamRead(ctx, 1, Http2Error.STREAM_CLOSED.code());
        Thread.sleep(1100);
        listener.onRstStreamRead(ctx, 1, Http2Error.STREAM_CLOSED.code());
        verify(frameListener, times(2)).onRstStreamRead(eq(ctx), anyInt(), eq(Http2Error.STREAM_CLOSED.code()));
    }


	public static Value get(Object value) {
		if (value == null) {
			return NullValue.INSTANCE;
		}

		if (value instanceof Value) {
			return (Value)value;
		}

		if (value instanceof byte[]) {
			return new BytesValue((byte[])value);
		}

		if (value instanceof String) {
			return new StringValue((String)value);
		}

		if (value instanceof Integer) {
			return new IntegerValue((Integer)value);
		}

		if (value instanceof Long) {
			return new LongValue((Long)value);
		}

		if (value instanceof List<?>) {
			return new ListValue((List<?>)value);
		}

		if (value instanceof Map<?,?>) {
			return new MapValue((Map<?,?>)value);
		}

		if (value instanceof Double) {
			return new DoubleValue((Double)value);
		}

		if (value instanceof Float) {
			return new FloatValue((Float)value);
		}

		if (value instanceof Short) {
			return new ShortValue((Short)value);
		}

		if (value instanceof Boolean) {
			if (UseBoolBin) {
				return new BooleanValue((Boolean)value);
			}
			else {
				return new BoolIntValue((Boolean)value);
			}
		}

		if (value instanceof Byte) {
			return new ByteValue((byte)value);
		}

		if (value instanceof Character) {
			return Value.get(((Character)value).charValue());
		}

		if (value instanceof Enum) {
        	return new StringValue(value.toString());
		}

		if (value instanceof UUID) {
			return new StringValue(value.toString());
		}

		if (value instanceof ByteBuffer) {
			ByteBuffer bb = (ByteBuffer)value;
			return new BytesValue(bb.array());
		}

		throw new AerospikeException("Unsupported type: " + value.getClass().getName());
	}

The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid.


	public void packObject(Object obj) {
		if (obj == null) {
			packNil();
			return;
		}

		if (obj instanceof Value) {
			Value value = (Value)obj;
			value.pack(this);
			return;
		}

		if (obj instanceof byte[]) {
			packParticleBytes((byte[])obj);
			return;
		}

		if (obj instanceof String) {
			packParticleString((String)obj);
			return;
		}

		if (obj instanceof Integer) {
			packInt((Integer)obj);
			return;
		}

		if (obj instanceof Long) {
			packLong((Long)obj);
			return;
		}

		if (obj instanceof List<?>) {
			packList((List<?>)obj);
			return;
		}

		if (obj instanceof Map<?,?>) {
			packMap((Map<?,?>)obj);
			return;
		}

		if (obj instanceof Double) {
			packDouble((Double)obj);
			return;
		}

		if (obj instanceof Float) {
			packFloat((Float)obj);
			return;
		}

		if (obj instanceof Short) {
			packInt((Short)obj);
			return;
		}

		if (obj instanceof Boolean) {
			packBoolean((Boolean)obj);
			return;
		}

		if (obj instanceof Byte) {
			packInt(((Byte)obj) & 0xff);
			return;
		}

		if (obj instanceof Character) {
			packInt(((Character)obj).charValue());
			return;
		}

		if (obj instanceof Enum) {
			packString(obj.toString());
			return;
		}

		if (obj instanceof UUID) {
			packString(obj.toString());
			return;
		}

		if (obj instanceof ByteBuffer) {
			packByteBuffer((ByteBuffer) obj);
			return;
		}

		throw new AerospikeException("Unsupported type: " + obj.getClass().getName());
	}


	public void runListRangeExample(AerospikeClient client, Parameters params) {
		Key key = new Key(params.namespace, params.set, "mapkey");
		String binName = "mapbin";

		// Delete record if it already exists.
		client.delete(params.writePolicy, key);

		List<Value> l1 = new ArrayList<Value>();
		l1.add(Value.get(new GregorianCalendar(2018, 1, 1).getTimeInMillis()));
		l1.add(Value.get(1));

		List<Value> l2 = new ArrayList<Value>();
		l2.add(Value.get(new GregorianCalendar(2018, 1, 2).getTimeInMillis()));
		l2.add(Value.get(2));

		List<Value> l3 = new ArrayList<Value>();
		l3.add(Value.get(new GregorianCalendar(2018, 2, 1).getTimeInMillis()));
		l3.add(Value.get(3));

		List<Value> l4 = new ArrayList<Value>();
		l4.add(Value.get(new GregorianCalendar(2018, 2, 2).getTimeInMillis()));
		l4.add(Value.get(4));

		List<Value> l5 = new ArrayList<Value>();
		l5.add(Value.get(new GregorianCalendar(2018, 2, 5).getTimeInMillis()));
		l5.add(Value.get(5));

		Map<Value,Value> inputMap = new HashMap<Value,Value>();
		inputMap.put(Value.get("Charlie"), Value.get(l1));
		inputMap.put(Value.get("Jim"), Value.get(l2));
		inputMap.put(Value.get("John"), Value.get(l3));
		inputMap.put(Value.get("Harry"), Value.get(l4));
		inputMap.put(Value.get("Bill"), Value.get(l5));

		// Write values to empty map.
		Record record = client.operate(params.writePolicy, key,
				MapOperation.putItems(MapPolicy.Default, binName, inputMap)
				);

		console.info("Record: " + record);

		List<Value> end = new ArrayList<Value>();
		end.add(Value.get(new GregorianCalendar(2018, 2, 2).getTimeInMillis()));
		end.add(Value.getAsNull());

		// Delete values < end.
		record = client.operate(params.writePolicy, key,
				MapOperation.removeByValueRange(binName, null, Value.get(end), MapReturnType.COUNT)
				);

		console.info("Record: " + record);
	}


	public void addNullValue() {
		Version version = Version.getServerVersion(client, null);

		// Do not run on servers < 3.6.1
		if (version.isLess(3, 6, 1)) {
			return;
		}

		Key key = new Key(args.namespace, args.set, "addkey");
		String binName = "addbin";

		// Delete record if it already exists.
		client.delete(null, key);

		Bin bin = new Bin(binName, Value.get((Object)null));

		AerospikeException ae = assertThrows(AerospikeException.class, new ThrowingRunnable() {
			public void run() {
				client.add(null, key, bin);
			}
		});

		assertEquals(ae.getResultCode(), ResultCode.PARAMETER_ERROR);
	}


  public static void validateString(final String what, final String s, String specials) {
    if (s == null) {
      throw new BadRequestException("Invalid " + what + ": null");
    } else if ("".equals(s)) {
      throw new BadRequestException("Invalid " + what + ": empty string");
    }
    final int n = s.length();
    for (int i = 0; i < n; i++) {
      final char c = s.charAt(i);
      if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
          || ('0' <= c && c <= '9') || c == '-' || c == '_' || c == '.'
          || c == '/' || Character.isLetter(c) || specials.indexOf(c) != -1)) {
        throw new BadRequestException("Invalid " + what
            + " (\"" + s + "\"): illegal character: " + c);
      }
    }
  }

Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')

The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.


  static void validateString(final String what, final String s) {
    validateString(what, s, "");
  }


  static void setPlotDimensions(final HttpQuery query, final Plot plot) {
    String wxh = query.getQueryStringParam("wxh");
    if (wxh != null && !wxh.isEmpty()) {
      wxh = URLDecoder.decode(wxh.trim());
      validateString("wxh", wxh);
      if (!WXH_VALIDATOR.matcher(wxh).find()) {
        throw new IllegalArgumentException("'wxh' was invalid. "
            + "Must satisfy the pattern " + WXH_VALIDATOR.toString());
      }
      final int wxhlength = wxh.length();
      if (wxhlength < 7) {  // 100x100 minimum.
        throw new BadRequestException("Parameter wxh too short: " + wxh);
      }
      final int x = wxh.indexOf('x', 3);  // Start at 2 as min size is 100x100
      if (x < 0) {
        throw new BadRequestException("Invalid wxh parameter: " + wxh);
      }
      try {
        final short width = Short.parseShort(wxh.substring(0, x));
        final short height = Short.parseShort(wxh.substring(x + 1, wxhlength));
        try {
          plot.setDimensions(width, height);
        } catch (IllegalArgumentException e) {
          throw new BadRequestException("Invalid wxh parameter: " + wxh + ", "
                                        + e.getMessage());
        }
      } catch (NumberFormatException e) {
        throw new BadRequestException("Can't parse wxh '" + wxh + "': "
                                      + e.getMessage());
      }
    }
  }


  public void setColorParams() throws Exception {
    assertPlotParam("bgcolor", "x000000");
    assertPlotParam("bgcolor", "XDEADBE");
    assertPlotParam("bgcolor", "%58DEADBE");
    assertInvalidPlotParam("bgcolor", "XDEADBEF");
    assertInvalidPlotParam("bgcolor", "%5BDEADBE");
    assertInvalidPlotParam("bgcolor", "xBDE%0AAD");

    assertPlotParam("fgcolor", "x000000");
    assertPlotParam("fgcolor", "XDEADBE");
    assertPlotParam("fgcolor", "%58DEADBE");
    assertInvalidPlotParam("fgcolor", "XDEADBEF");
    assertInvalidPlotParam("fgcolor", "%5BDEADBE");
    assertInvalidPlotParam("fgcolor", "xBDE%0AAD");
  }


  public void setWXH() throws Exception {
    assertPlotDimension("wxh",  "720x640");
    assertInvalidPlotDimension("wxh", "720%0ax640");
  }


  public void setKeyParams() throws Exception {
    assertPlotParam("key", "out");
    assertPlotParam("key", "left");
    assertPlotParam("key", "top");
    assertPlotParam("key", "center");
    assertPlotParam("key", "right");
    assertPlotParam("key", "horiz");
    assertPlotParam("key", "box");
    assertPlotParam("key", "bottom");
    assertInvalidPlotParam("key", "out%20right%20top%0aset%20yrange%20[33:system(%20");
    assertInvalidPlotParam("key", "%3Bsystem%20%22cat%20/home/ubuntuvm/secret.txt%20%3E/tmp/secret.txt%22%20%22");
  }


  public void setLabelParams() throws Exception {
    assertPlotParam("ylabel", "This is good");
    assertPlotParam("ylabel", " and so Is this - _ yay");
    assertInvalidPlotParam("ylabel", "system(%20no%0anewlines");
    assertInvalidPlotParam("title", "system(%20no%0anewlines");
    assertInvalidPlotParam("y2label", "system(%20no%0anewlines");
  }


  public void setSmoothParams() throws Exception {
    assertPlotParam("smooth", "unique");
    assertPlotParam("smooth", "frequency");
    assertPlotParam("smooth", "fnormal");
    assertPlotParam("smooth", "cumulative");
    assertPlotParam("smooth", "cnormal");
    assertPlotParam("smooth", "bins");
    assertPlotParam("smooth", "csplines");
    assertPlotParam("smooth", "acsplines");
    assertPlotParam("smooth", "mcsplines");
    assertPlotParam("smooth", "bezier");
    assertPlotParam("smooth", "sbezier");
    assertPlotParam("smooth", "unwrap");
    assertPlotParam("smooth", "zsort");
    assertInvalidPlotParam("smooth", "bezier%20system(%20");
    assertInvalidPlotParam("smooth", "fnormal%0asystem(%20");
  }


  public void setFormatParams() throws Exception {
    assertPlotParam("yformat", "%25.2f");
    assertPlotParam("y2format", "%25.2f");
    assertPlotParam("xformat", "%25.2f");
    assertPlotParam("yformat", "%253.0em");
    assertPlotParam("yformat", "%253.0em%25%25");
    assertPlotParam("yformat", "%25.2f seconds");
    assertPlotParam("yformat", "%25.0f ms");
    assertInvalidPlotParam("yformat", "%252.system(%20");
    assertInvalidPlotParam("yformat", "%252.%0asystem(%20");
  }


  private static void assertPlotDimension(String param, String value) {
    Plot plot = mock(Plot.class);
    HttpQuery query = mock(HttpQuery.class);
    when(query.getQueryStringParam(param)).thenReturn(value);
    GraphHandler.setPlotParams(query, plot);
  }


  public void setStyleParams() throws Exception {
    assertPlotParam("style", "linespoint");
    assertPlotParam("style", "points");
    assertPlotParam("style", "circles");
    assertPlotParam("style", "dots");
    assertInvalidPlotParam("style", "dots%20%0a[33:system(%20");
    assertInvalidPlotParam("style", "%3Bsystem%20%22cat%20/home/ubuntuvm/secret.txt%20%3E/tmp/secret.txt%22%20%22\"");
  }


  private static void assertInvalidPlotDimension(String param, String value) {
    Plot plot = mock(Plot.class);
    HttpQuery query = mock(HttpQuery.class);
    when(query.getQueryStringParam(param)).thenReturn(value);
    try {
      GraphHandler.setPlotDimensions(query, plot);
      fail("Expected BadRequestException");
    } catch (BadRequestException e) { }
  }


  public void setYRangeParams() throws Exception {
    assertPlotParam("yrange","[0:1]");
    assertPlotParam("yrange", "[:]");
    assertPlotParam("yrange", "[:0]");
    assertPlotParam("yrange", "[:42]");
    assertPlotParam("yrange", "[:-42]");
    assertPlotParam("yrange", "[:0.8]");
    assertPlotParam("yrange", "[:-0.8]");
    assertPlotParam("yrange", "[:42.4]");
    assertPlotParam("yrange", "[:-42.4]");
    assertPlotParam("yrange", "[:4e4]");
    assertPlotParam("yrange", "[:-4e4]");
    assertPlotParam("yrange", "[:4e-4]");
    assertPlotParam("yrange", "[:-4e-4]");
    assertPlotParam("yrange", "[:4.2e4]");
    assertPlotParam("yrange", "[:-4.2e4]");
    assertPlotParam("yrange", "[0:]");
    assertPlotParam("yrange", "[5:]");
    assertPlotParam("yrange", "[-5:]");
    assertPlotParam("yrange", "[0.5:]");
    assertPlotParam("yrange", "[-0.5:]");
    assertPlotParam("yrange", "[10.5:]");
    assertPlotParam("yrange", "[-10.5:]");
    assertPlotParam("yrange", "[10e5:]");
    assertPlotParam("yrange", "[-10e5:]");
    assertPlotParam("yrange", "[10e-5:]");
    assertPlotParam("yrange", "[-10e-5:]");
    assertPlotParam("yrange", "[10.1e-5:]");
    assertPlotParam("yrange", "[-10.1e-5:]");
    assertPlotParam("yrange", "[-10.1e-5:-10.1e-6]");
    assertInvalidPlotParam("yrange", "[33:system('touch /tmp/poc.txt')]");
    assertInvalidPlotParam("y2range", "[42:%0a[33:system('touch /tmp/poc.txt')]");
  }


public void badRequest(final BadRequestException exception) {
    logWarn("Bad Request on " + request().getUri() + ": " + exception.getMessage());
    if (this.api_version > 0) {
      // always default to the latest version of the error formatter since we
      // need to return something
      switch (this.api_version) {
        case 1:
        default:
          sendReply(exception.getStatus(), serializer.formatErrorV1(exception));
      }
      return;
    }
    if (hasQueryStringParam("json")) {
      final StringBuilder buf = new StringBuilder(10 +
          exception.getDetails().length());
      buf.append("{\"err\":\"");
      HttpQuery.escapeJson(exception.getMessage(), buf);
      buf.append("\"}");
      sendReply(HttpResponseStatus.BAD_REQUEST, buf);
    } else {
      String response = "";
      if (exception.getMessage() != null) {
        response = HtmlEscapers.htmlEscaper().escape(exception.getMessage());
      }
      sendReply(HttpResponseStatus.BAD_REQUEST,
                makePage("Bad Request", "Looks like it's your fault this time",
                         "
"
                         + "
Bad Request
"
                         + "Sorry but your request was rejected as being"
                         + " invalid.

"
                         + "The reason provided was:
"
                         + response
                         + "

"));
    }
  }


public void internalError(final Exception cause) {
    logError("Internal Server Error on " + request().getUri(), cause);

    if (this.api_version > 0) {
      // always default to the latest version of the error formatter since we
      // need to return something
      switch (this.api_version) {
        case 1:
        default:
          sendReply(HttpResponseStatus.INTERNAL_SERVER_ERROR,
              serializer.formatErrorV1(cause));
      }
      return;
    }

    ThrowableProxy tp = new ThrowableProxy(cause);
    tp.calculatePackagingData();
    final String pretty_exc = ThrowableProxyUtil.asString(tp);
    tp = null;
    if (hasQueryStringParam("json")) {
      // 32 = 10 + some extra space as exceptions always have \t's to escape.
      final StringBuilder buf = new StringBuilder(32 + pretty_exc.length());
      buf.append("{\"err\":\"");
      HttpQuery.escapeJson(pretty_exc, buf);
      buf.append("\"}");
      sendReply(HttpResponseStatus.INTERNAL_SERVER_ERROR, buf);
    } else {
      String response = "";
      if (pretty_exc != null) {
        response = HtmlEscapers.htmlEscaper().escape(pretty_exc);
      }
      sendReply(HttpResponseStatus.INTERNAL_SERVER_ERROR,
                makePage("Internal Server Error", "Houston, we have a problem",
                         "
"
                         + "
Internal Server Error
"
                         + "Oops, sorry but your request failed due to a"
                         + " server error.

"
                         + "Please try again in 30 seconds.
"
                         + response
                         + "

"));
    }
  }


  public void internalErrorDeprecatedHTMLEscaped() {
    HttpQuery query = NettyMocks.getQuery(tsdb, "");
    query.internalError(new Exception("<script>alert(document.cookie)</script>"));

    assertEquals(HttpResponseStatus.INTERNAL_SERVER_ERROR,
        query.response().getStatus());
    assertTrue(query.response().getContent().toString(Charset.forName("UTF-8")).contains(
        "<script>alert(document.cookie)</script>"
    ));
  }


  public void badRequestDeprecatedHTMLEscaped() {
    HttpQuery query = NettyMocks.getQuery(tsdb, "/");
    query.badRequest(new BadRequestException("<script>alert(document.cookie)</script>"));

    assertEquals(HttpResponseStatus.BAD_REQUEST, query.response().getStatus());
    assertTrue(query.response().getContent().toString(Charset.forName("UTF-8")).contains(
        "The reason provided was:<blockquote><script>alert(document.cookie)</script>"
    ));
  }


  public void executeNSU() throws Exception {
    final DeferredGroupException dge = mock(DeferredGroupException.class);
    when(dge.getCause()).thenReturn(new NoSuchUniqueName("foo", "metrics"));

    when(query_result.configureFromQuery((TSQuery)any(), anyInt()))
      .thenReturn(Deferred.fromError(dge));

    final HttpQuery query = NettyMocks.getQuery(tsdb,
        "/api/query?start=1h-ago&m=sum:sys.cpu.user");
    rpc.execute(tsdb, query);
    assertEquals(HttpResponseStatus.BAD_REQUEST, query.response().getStatus());
    final String json =
        query.response().getContent().toString(Charset.forName("UTF-8"));
    assertTrue(json.contains("No such name for 'foo': 'metrics'"));
  }


  public void postQueryNoMetricBadRequest() throws Exception {
    final DeferredGroupException dge = mock(DeferredGroupException.class);
    when(dge.getCause()).thenReturn(new NoSuchUniqueName("foo", "metrics"));

    when(query_result.configureFromQuery((TSQuery)any(), anyInt()))
      .thenReturn(Deferred.fromError(dge));

    HttpQuery query = NettyMocks.postQuery(tsdb, "/api/query",
        "{\"start\":1425440315306,\"queries\":" +
          "[{\"metric\":\"nonexistent\",\"aggregator\":\"sum\",\"rate\":true," +
          "\"rateOptions\":{\"counter\":false}}]}");
    rpc.execute(tsdb, query);
    assertEquals(HttpResponseStatus.BAD_REQUEST, query.response().getStatus());
    final String json =
        query.response().getContent().toString(Charset.forName("UTF-8"));
    assertTrue(json.contains("No such name for 'foo': 'metrics'"));
  }


    protected boolean validateIssuerAudienceAndAzp(@NonNull JwtClaims claims,
                                                   @NonNull String iss,
                                                   @NonNull List<String> audiences,
                                                   @NonNull String clientId,
                                                   @NonNull OpenIdClientConfiguration openIdClientConfiguration) {
        if (openIdClientConfiguration.getIssuer().isPresent()) {
            Optional<URL> issuerOptional = openIdClientConfiguration.getIssuer();
            if (issuerOptional.isPresent()) {
                String issuer = issuerOptional.get().toString();
                return issuer.equalsIgnoreCase(iss) &&
                        audiences.contains(clientId) &&
                                validateAzp(claims, clientId, audiences);
            }
        }
        return false;
    }


    public static Config setComments(Config config, String path, List<String> comments) {
        return config.withValue(path, setComments(config.getValue(path), comments));
    }


    public static ConfigValue setComments(ConfigValue value, List<String> comments) {
        if (value.origin() instanceof SimpleConfigOrigin && value instanceof AbstractConfigValue) {
            return ((AbstractConfigValue) value).withOrigin(
                ((SimpleConfigOrigin) value.origin()).setComments(comments)
            );
        } else {
            return value;
        }
    }


    private OpenComputersConfigCommentManipulationHook() {

    }


    public boolean matches(InetAddress address) {
        byte[] entry = address.getAddress();
        if (entry.length != min.length) return false;

        for (int i = 0; i < entry.length; i++) {
            int value = 0xFF & entry[i];
            if (value < (0xFF & min[i]) || value > (0xFF & max[i])) return false;
        }

        return true;
    }


    public static InetAddressRange parse(String addressStr, String prefixSizeStr) {
        int prefixSize;
        try {
            prefixSize = Integer.parseInt(prefixSizeStr);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException(String.format("Malformed address range entry '%s': Cannot extract size of CIDR mask from '%s'.",
                    addressStr + '/' + prefixSizeStr, prefixSizeStr));
        }

        InetAddress address;
        try {
            address = InetAddresses.forString(addressStr);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException(String.format("Malformed address range entry '%s': Cannot extract IP address from '%s'.",
                    addressStr + '/' + prefixSizeStr, addressStr));
        }

        // Mask the bytes of the IP address.
        byte[] minBytes = address.getAddress(), maxBytes = address.getAddress();
        int size = prefixSize;
        for (int i = 0; i < minBytes.length; i++) {
            if (size <= 0) {
                minBytes[i] = (byte) 0;
                maxBytes[i] = (byte) 0xFF;
            } else if (size < 8) {
                minBytes[i] = (byte) (minBytes[i] & 0xFF << (8 - size));
                maxBytes[i] = (byte) (maxBytes[i] | ~(0xFF << (8 - size)));
            }

            size -= 8;
        }

        return new InetAddressRange(minBytes, maxBytes);
    }


    InetAddressRange(byte[] min, byte[] max) {
        this.min = min;
        this.max = max;
    }


        public boolean matches(InetAddress socketAddress) {
            return
                socketAddress.isAnyLocalAddress()      // 0.0.0.0, ::0
                || socketAddress.isLoopbackAddress()   // 127.0.0.0/8, ::1
                || socketAddress.isLinkLocalAddress()  // 169.254.0.0/16, fe80::/10
                || socketAddress.isSiteLocalAddress()  // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fec0::/10
                || socketAddress.isMulticastAddress()  // 224.0.0.0/4, ff00::/8
                || isUniqueLocalAddress(socketAddress) // fd00::/8
                || additionalAddresses.contains(socketAddress);
        }


        private boolean isUniqueLocalAddress(InetAddress address) {
            // ULA is actually defined as fc00::/7 (so both fc00::/8 and fd00::/8). However, only the latter is actually
            // defined right now, so let's be conservative.
            return address instanceof Inet6Address && (address.getAddress()[0] & 0xff) == 0xfd;
        }


    void testSymlinkTar() throws Exception {
        TarArchiver archiver = (TarArchiver) lookup(Archiver.class, "tar");
        archiver.setLongfile(TarLongFileMode.posix);

        File dummyContent = getTestFile("src/test/resources/symlinks/src");
        archiver.addDirectory(dummyContent);
        final File archiveFile = new File("target/output/symlinks.tar");
        archiver.setDestFile(archiveFile);
        archiver.createArchive();
        File output = getTestFile("target/output/untaredSymlinks");
        output.mkdirs();
        TarUnArchiver unarchiver = (TarUnArchiver) lookup(UnArchiver.class, "tar");
        unarchiver.setSourceFile(archiveFile);
        unarchiver.setDestFile(output);
        unarchiver.extract();
        // second unpacking should also work
        unarchiver.extract();
    }

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.


The product, when opening a file or directory, does not sufficiently account for when the file is a symbolic link that resolves to a target outside of the intended control sphere. This could allow an attacker to cause the product to operate on unauthorized files.


    void testSymlinkZip() throws Exception {
        ZipArchiver archiver = (ZipArchiver) lookup(Archiver.class, "zip");

        File dummyContent = getTestFile("src/test/resources/symlinks/src");
        archiver.addDirectory(dummyContent);
        final File archiveFile = new File("target/output/symlinks.zip");
        archiveFile.delete();
        archiver.setDestFile(archiveFile);
        archiver.createArchive();

        File output = getTestFile("target/output/unzippedSymlinks");
        output.mkdirs();
        ZipUnArchiver unarchiver = (ZipUnArchiver) lookup(UnArchiver.class, "zip");
        unarchiver.setSourceFile(archiveFile);
        unarchiver.setDestFile(output);
        unarchiver.extract();
        // second unpacking should also work
        unarchiver.extract();
    }


    void testNonExistingSymlink() throws Exception {
        File zipFile = new File("src/test/resources/symlinks/non_existing_symlink.zip");
        ZipUnArchiver unArchiver = getZipUnArchiver(zipFile);
        String tmpdir = Files.createTempDirectory("tmpe_extract").toFile().getAbsolutePath();
        unArchiver.setDestDirectory(new File(tmpdir));
        ArchiverException exception = assertThrows(ArchiverException.class, unArchiver::extract);
        assertEquals("Entry is outside of the target directory (entry1)", exception.getMessage());
    }


    public static Map processResponse(Response samlResponse, String target)
        throws SAMLException {
        List assertions = null;
        SAMLServiceManager.SOAPEntry partnerdest = null;
        Subject assertionSubject = null;

        // verify the signature
        boolean isSignedandValid = verifySignature(samlResponse);
        if (!isSignedandValid) {
            throw new SAMLException(bundle.getString("invalidResponse"));
        }

        // check Assertion and get back a Map of relevant data including,
        // Subject, SOAPEntry for the partner and the List of Assertions.
        Map ssMap = verifyAssertionAndGetSSMap(samlResponse);
        if (debug.messageEnabled()) {
            debug.message("processResponse: ssMap = " + ssMap);
        }

        if (ssMap == null) {
            throw new SAMLException(bundle.getString("invalidAssertion"));
        }
        assertionSubject = (com.sun.identity.saml.assertion.Subject)
            ssMap.get(SAMLConstants.SUBJECT);
        if (assertionSubject == null) {
            throw new SAMLException(bundle.getString("nullSubject"));
        }

        partnerdest = (SAMLServiceManager.SOAPEntry)ssMap
            .get(SAMLConstants.SOURCE_SITE_SOAP_ENTRY);
        if (partnerdest == null) {
            throw new SAMLException(bundle.getString("failedAccountMapping"));
        }

        assertions = (List)ssMap.get(SAMLConstants.POST_ASSERTION);
        Map sessMap = null;
        try {
            sessMap = getAttributeMap(partnerdest, assertions,
                assertionSubject, target);
        } catch (Exception se) {
            debug.error("SAMLUtils.processResponse :" , se);
            throw new SAMLException(
                bundle.getString("failProcessResponse"));
        }
        return sessMap;
    }


    public static boolean verifyResponse(Response response,
    String requestUrl, HttpServletRequest request) {
        if(!response.isSigned()) {
            debug.message("verifyResponse: Response is not signed");
            return false;
        }
        if (!response.isSignatureValid()) {
            debug.message("verifyResponse: Response's signature is invalid.");
            return false;
        }

        // check Recipient == this server's POST profile URL(requestURL)
        String recipient = response.getRecipient();
        if ((recipient == null) || (recipient.length() == 0) ||
        ((!equalURL(recipient, requestUrl)) &&
        (!equalURL(recipient,getLBURL(requestUrl, request))))) {
            debug.error("verifyResponse : Incorrect Recipient.");
            return false;
        }

        // check status of the Response
        if (!response.getStatus().getStatusCode().getValue().endsWith(
        SAMLConstants.STATUS_CODE_SUCCESS_NO_PREFIX)) {
            debug.error("verifyResponse : Incorrect StatusCode value.");
            return false;
        }

        return true;
    }


    protected void untar(File destDir, InputStream inputStream) throws IOException {
        TarArchiveInputStream tin = new TarArchiveInputStream(inputStream);
        TarArchiveEntry tarEntry = null;

        while ((tarEntry = tin.getNextTarEntry()) != null) {
            File destEntry = new File(destDir, tarEntry.getName());
            if (!destEntry.toPath().normalize().startsWith(destDir.toPath().normalize())) {
                throw new IllegalArgumentException("Zip archives with files escaping their root directory are not allowed.");
            }
            File parent = destEntry.getParentFile();

            if (!parent.exists()) {
                parent.mkdirs();
            }

            if (tarEntry.isDirectory()) {
                destEntry.mkdirs();
            } else {
                FileOutputStream fout = new FileOutputStream(destEntry);
                try {
                    IOUtils.copy(tin, fout);
                } finally {
                    fout.close();
                }
            }
        }

        tin.close();
    }


    public void testUntarZipSlip() throws IOException {
        FileProjectManager manager = new FileProjectManagerStub(workspaceDir);

        File tempDir = TestUtils.createTempDirectory("openrefine-project-import-zip-slip-test");
        try {
            File subDir = new File(tempDir, "dest");
            InputStream stream = FileProjectManagerTests.class.getClassLoader().getResourceAsStream("zip-slip.tar");

            assertThrows(IllegalArgumentException.class, () -> manager.untar(subDir, stream));
        } finally {
            tempDir.delete();
        }
    }


    public URI toURI() {
        try {
            return new URI(
                    "jdbc:" + databaseType.toLowerCase(),
                    databaseHost + ((databasePort == 0) ? "" : (":" + databasePort)),
                    "/" + databaseName,
                    useSSL ? "useSSL=true" : null,
                    null
            );
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException(e);
        }
    }

Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component.


    public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection)
            throws DatabaseServiceException {
        try {

            // logger.info("connection::{}, forceNewConnection: {}", connection, forceNewConnection);

            if (connection != null && !forceNewConnection) {
                // logger.debug("connection closed::{}", connection.isClosed());
                if (!connection.isClosed()) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Returning existing connection::{}", connection);
                    }

                    return connection;
                }
            }

            Class.forName(type.getClassPath());
            DriverManager.setLoginTimeout(10);
            String dbURL = databaseConfiguration.toURI().toString();
            connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(),
                    databaseConfiguration.getDatabasePassword());

            if (logger.isDebugEnabled()) {
                logger.debug("*** Acquired New  connection for ::{} **** ", dbURL);
            }

            return connection;

        } catch (ClassNotFoundException e) {
            logger.error("Jdbc Driver not found", e);
            throw new DatabaseServiceException(e.getMessage());
        } catch (SQLException e) {
            logger.error("SQLException::Couldn't get a Connection!", e);
            throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());
        }
    }


    public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection)
            throws DatabaseServiceException {
        try {

            if (connection != null && !forceNewConnection) {
                // logger.info("connection closed::{}", connection.isClosed());
                if (!connection.isClosed()) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Returning existing connection::{}", connection);
                    }

                    return connection;
                }
            }
            String dbURL = databaseConfiguration.toURI().toString();
            Class.forName(type.getClassPath());

            // logger.info("*** type.getClassPath() ::{}, {}**** ", type.getClassPath());

            DriverManager.setLoginTimeout(10);

            connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(),
                    databaseConfiguration.getDatabasePassword());

            if (logger.isDebugEnabled()) {
                logger.debug("*** Acquired New  connection for ::{} **** ", dbURL);
            }

            return connection;

        } catch (ClassNotFoundException e) {
            logger.error("Jdbc Driver not found", e);
            throw new DatabaseServiceException(e.getMessage());
        } catch (SQLException e) {
            logger.error("SQLException::Couldn't get a Connection!", e);
            throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());
        }
    }


    public Connection getConnection(DatabaseConfiguration databaseConfiguration, boolean forceNewConnection)
            throws DatabaseServiceException {
        try {

            // logger.info("connection::{}, forceNewConnection: {}", connection, forceNewConnection);

            if (connection != null && !forceNewConnection) {
                // logger.info("connection closed::{}", connection.isClosed());
                if (!connection.isClosed()) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Returning existing connection::{}", connection);
                    }
                    return connection;
                }
            }

            Class.forName(type.getClassPath());
            DriverManager.setLoginTimeout(10);
            String dbURL = databaseConfiguration.toURI().toString();
            connection = DriverManager.getConnection(dbURL, databaseConfiguration.getDatabaseUser(),
                    databaseConfiguration.getDatabasePassword());

            logger.debug("*** Acquired New  connection for ::{} **** ", dbURL);

            return connection;

        } catch (ClassNotFoundException e) {
            logger.error("Jdbc Driver not found", e);
            throw new DatabaseServiceException(e.getMessage());
        } catch (SQLException e) {
            logger.error("SQLException::Couldn't get a Connection!", e);
            throw new DatabaseServiceException(true, e.getSQLState(), e.getErrorCode(), e.getMessage());
        }
    }


    public static String getDatabaseUrl(DatabaseConfiguration dbConfig) {
        try {
            URI uri = new URI("jdbc:" + dbConfig.getDatabaseType().toLowerCase(), dbConfig.getDatabaseName(), null);
            return uri.toASCIIString();
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException(e);
        }
    }


    public void testToURI() {
        DatabaseConfiguration config = new DatabaseConfiguration();
        config.setDatabaseType("mysql");
        config.setDatabaseHost("my.host");
        // maliciously crafted database name which attempts to enable local file reads for an exploit
        config.setDatabaseName("test?allowLoadLocalInfile=true#");

        String url = config.toURI().toString();
        // the database name is escaped, preventing the exploit
        assertEquals(url, "jdbc:mysql://my.host/test%3FallowLoadLocalInfile=true%23");
    }


    protected AbstractRequestContextBuilder(boolean server, RpcRequest rpcReq, URI uri) {
        this.server = server;
        req = null;
        this.rpcReq = requireNonNull(rpcReq, "rpcReq");
        method = HttpMethod.POST;

        requireNonNull(uri, "uri");
        authority = firstNonNull(uri.getRawAuthority(), FALLBACK_AUTHORITY);
        sessionProtocol = getSessionProtocol(uri);

        if (server) {
            String path = uri.getRawPath();
            final String query = uri.getRawQuery();
            if (query != null) {
                path += '?' + query;
            }
            final RequestTarget reqTarget = RequestTarget.forServer(path);
            if (reqTarget == null) {
                throw new IllegalArgumentException("invalid uri: " + uri);
            }
            this.reqTarget = reqTarget;
        } else {
            reqTarget = DefaultRequestTarget.createWithoutValidation(
                    RequestTargetForm.ORIGIN, null, null,
                    uri.getRawPath(), uri.getRawPath(), uri.getRawQuery(), uri.getRawFragment());
        }
    }


    public Boolean allowSemicolonInPathComponent() {
        return false;
    }


    public static boolean allowSemicolonInPathComponent() {
        return ALLOW_SEMICOLON_IN_PATH_COMPONENT;
    }


    default Boolean allowSemicolonInPathComponent() {
        return null;
    }


    static RequestTarget forServer(String reqTarget) {
        requireNonNull(reqTarget, "reqTarget");
        return DefaultRequestTarget.forServer(reqTarget, Flags.allowSemicolonInPathComponent(),
                                              Flags.allowDoubleDotsInQueryString());
    }


    public Boolean allowSemicolonInPathComponent() {
        return getBoolean("allowSemicolonInPathComponent");
    }


    public static RequestTarget forServer(String reqTarget, boolean allowSemicolonInPathComponent,
                                          boolean allowDoubleDotsInQueryString) {
        final RequestTarget cached = RequestTargetCache.getForServer(reqTarget);
        if (cached != null) {
            return cached;
        }

        return slowForServer(reqTarget, allowSemicolonInPathComponent, allowDoubleDotsInQueryString);
    }


    private DefaultRequestTarget(RequestTargetForm form, @Nullable String scheme, @Nullable String authority,
                                 String path, String maybePathWithMatrixVariables,
                                 @Nullable String query, @Nullable String fragment) {

        assert (scheme != null && authority != null) ||
               (scheme == null && authority == null) : "scheme: " + scheme + ", authority: " + authority;

        this.form = form;
        this.scheme = scheme;
        this.authority = authority;
        this.path = path;
        this.maybePathWithMatrixVariables = maybePathWithMatrixVariables;
        this.query = query;
        this.fragment = fragment;
    }