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

org.kitei.testing.lessio.LessIOFilesystemDelegate Maven / Gradle / Ivy

The newest version!
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kitei.testing.lessio;

import static java.lang.String.format;

import static org.kitei.testing.lessio.LessIOUtils.checkNotNull;
import static org.kitei.testing.lessio.LessIOUtils.createGlobMatcher;
import static org.kitei.testing.lessio.LessIOUtils.findAnnotation;
import static org.kitei.testing.lessio.LessIOUtils.hasAnnotations;

import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

class LessIOFilesystemDelegate
{
    private final LessIOContext context;
    private final AtomicReference> classpathHolder;

    private final Map whitelistCache = new ConcurrentHashMap<>();

    private final AtomicBoolean tmpDirWarningFlag = new AtomicBoolean();
    private final AtomicBoolean fileDescriptorWarningFlag = new AtomicBoolean();

    LessIOFilesystemDelegate(final LessIOContext context, final AtomicReference> classpathHolder)
    {
        this.context = context;
        this.classpathHolder = classpathHolder;

        for (final Path whitelistedPath : context.getWhitelistedPaths()) {
            whitelistCache.put(whitelistedPath, Boolean.TRUE);
        }
    }

    private void tmpDirWarning()
    {
        if (tmpDirWarningFlag.compareAndSet(false, true)) {
            System.err.println("*************************************************************");
            System.err.println("*                                                           *");
            System.err.println("* Usage of %TMP_DIR% is deprecated, use @AllowTmpDirAccess! *");
            System.err.println("*                                                           *");
            System.err.println("*************************************************************");
        }
    }

    private void fileDescriptorWarning()
    {
        if (fileDescriptorWarningFlag.compareAndSet(false, true)) {
            System.err.println("************************************************************");
            System.err.println("*                                                          *");
            System.err.println("* Usage of %FD% is deprecated, use @AllowFileDescriptorIO! *");
            System.err.println("*                                                          *");
            System.err.println("************************************************************");
        }
    }

    boolean checkFilesystemAccess(final Path path)
        throws IOException
    {
        final Boolean result = whitelistCache.get(path);
        if (result != null) {
            return result;
        }

        for (final Path whitelistedPath : context.getWhitelistedPaths()) {
            // Files.isSameFile accesses the file system so it can not be used here.
            if (whitelistedPath.equals(path)) {
                whitelistCache.put(path, Boolean.TRUE);
                return true;
            }
        }

        for (final PathMatcher whitelistedPathGlob : context.getWhitelistedPathGlobs()) {
            if (whitelistedPathGlob.matches(path)) {
                whitelistCache.put(path, Boolean.TRUE);
                return true;
            }
        }

        /*
         * Although this is an expensive operation, it needs to be here, in a
         * suboptimal location to avoid ClassCircularityErrors that can occur when
         * attempting to load an anonymous class.
         */
        for (final String classpathReference : classpathHolder.get()) {
            if (path.toString().startsWith(classpathReference)) {
                // Files on the CLASSPATH are always allowed.
                whitelistCache.put(path, Boolean.TRUE);
                return true;
            }
        }

        return false;
    }

    LessIOPredicate getFileAccessPredicate(final Path path, final String description)
    {
        return new FilesystemFileAccessPredicate(path, description);
    }

    LessIOPredicate getFileDescriptorPredicate(final FileDescriptor fd, final String description)
    {
        return new FilesystemFileDescriptorPredicate(fd, description);
    }

    private class FilesystemFileAccessPredicate implements LessIOPredicate
    {
        private final Path path;
        private final String description;

        private final boolean tmpFile;

        private FilesystemFileAccessPredicate(final Path path, final String description)
        {
            this.path = path;
            this.description = description;
            this.tmpFile = path.startsWith(LessIOUtils.TMP_PATH);
        }

        @Override
        public boolean check(final Class clazz)
            throws Exception
        {
            if (tmpFile && hasAnnotations(clazz, AllowTmpDirAccess.class)) {
                return true;
            }

            final AllowLocalFileAccess annotation = LessIOUtils.findAnnotation(clazz, AllowLocalFileAccess.class);
            if (annotation == null) {
                return false;
            }

            final String[] paths = annotation.paths();
            if (paths == null) {
                return false;
            }

            for (final String p : paths) {
                if (p.equals("*")) {
                    return true;
                }

                PathMatcher tmpMatcher = null;

                // not here checked.
                if (p.equals("%FD%")) {
                    fileDescriptorWarning();
                    continue;
                }

                if (p.equals("%TMP_DIR%")) {
                    tmpDirWarning();
                    if (tmpFile) {
                        return true;
                    }
                }
                else if (p.startsWith("%TMP_DIR%/")) {
                    tmpDirWarning();
                    tmpMatcher = createGlobMatcher(LessIOUtils.TMP_PATH.resolve(p.substring(10)));
                }

                if (tmpMatcher != null) {
                    if (tmpMatcher.matches(path)) {
                        return true;
                    }
                    else {
                        // next check
                        continue;
                    }
                }

                final Path annotationPath = Paths.get(p);

                // Files.isSameFile accesses the file system so it can not be used here.
                if (annotationPath.equals(path)) {
                    return true;
                }

                final PathMatcher matcher = createGlobMatcher(annotationPath);
                if (matcher.matches(path)) {
                    return true;
                }
            }

            return false;
        }

        @Override
        public String toString()
        {
            return format("@AllowLocalFileAccess for %s (%s)", path, description);
        }
    }

    private class FilesystemFileDescriptorPredicate implements LessIOPredicate
    {
        private final String description;
        private final FileDescriptor fd;

        private FilesystemFileDescriptorPredicate(final FileDescriptor fd, final String description)
        {
            this.fd = checkNotNull(fd, "fd is null");
            this.description = description;
        }

        @Override
        public boolean check(final Class clazz)
            throws Exception
        {

            // AllowExternalProcess and AllowNetworkAccess imply @AllowLocalFileDescriptorIO
            // since it's required.
            if (hasAnnotations(clazz, AllowFileDescriptorIO.class, AllowExternalProcess.class, AllowNetworkAccess.class)) {
                return true;
            }

            final AllowLocalFileAccess annotation = findAnnotation(clazz, AllowLocalFileAccess.class);
            if (annotation == null) {
                return false;
            }

            final String[] paths = annotation.paths();
            if (paths == null) {
                return false;
            }

            for (final String p : paths) {
                if (p.equals("%FD%")) {
                    fileDescriptorWarning();
                    return true;
                }
            }
            return false;
        }

        @Override
        public String toString()
        {
            return format("@AllowLocalFileAccess for FileDescriptor(%s) (%s)", fd, description);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy