Merge pull request #41 from BulkSecurityGeneratorProjectV2/fix/JLL/temporary_file_local_information_disclosure

[SECURITY] Fix Temporary File Information Disclosure Vulnerability
diff --git a/.asf.yaml b/.asf.yaml
index c4067a6..5103f86 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -2,7 +2,17 @@
   description: "Apache Sling Feature Model Analyser"
   homepage: "https://sling.apache.org/"
   labels:
-    - java
-    - sling
-    - osgi
-    - osgi-feature-model
+  - "java"
+  - "sling"
+  - "osgi"
+  - "osgi-feature-model"
+  autolink_jira:
+  - "SLING"
+  - "OAK"
+  - "JCR"
+  - "JCRVLT"
+  - "INFRA"
+  - "FELIX"
+  - "MNG"
+notifications:
+  jira_options: "link"
diff --git a/pom.xml b/pom.xml
index 945eefc..5466ef4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
     </parent>
 
     <artifactId>org.apache.sling.feature.analyser</artifactId>
-    <version>1.6.9-SNAPSHOT</version>
+    <version>2.0.3-SNAPSHOT</version>
 
     <name>Apache Sling Feature Model Analyser</name>
     <description>
@@ -37,13 +37,13 @@
 
     <properties>
         <sling.java.version>8</sling.java.version>
-        <project.build.outputTimestamp>1663698996</project.build.outputTimestamp>
+        <project.build.outputTimestamp>1699106219</project.build.outputTimestamp>
     </properties>
 
     <scm>
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-analyser.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-analyser.git</developerConnection>
-        <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-feature-analyser.git</url>
+        <url>https://github.com/apache/sling-org-apache-sling-feature-analyser.git</url>
       <tag>org.apache.sling.feature.analyser-1.6.4</tag>
    </scm>
 
@@ -91,9 +91,9 @@
     </build>
     <dependencies>
         <dependency>
-            <groupId>org.apache.geronimo.specs</groupId>
-            <artifactId>geronimo-json_1.1_spec</artifactId>
-            <version>1.2</version>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <version>2.0.2</version>
             <scope>provided</scope>
          </dependency>
          <dependency>
@@ -126,13 +126,13 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature</artifactId>
-            <version>1.2.28</version>
+            <version>2.0.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.cm.json</artifactId>
-            <version>1.0.6</version>
+            <version>2.0.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -170,7 +170,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.repoinit.parser</artifactId>
-            <version>1.8.0</version>
+            <version>1.9.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -195,12 +195,13 @@
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
-            <version>2.22.0</version>
+            <version>4.9.0</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.johnzon</groupId>
             <artifactId>johnzon-core</artifactId>
+            <classifier>jakarta</classifier>
             <version>1.2.14</version>
             <scope>test</scope>
         </dependency>
@@ -208,7 +209,7 @@
             <!-- Used by some tests -->
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.framework</artifactId>
-            <version>6.0.3</version>
+            <version>7.0.5</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git a/readme.md b/readme.md
index b6b91b4..274db66 100644
--- a/readme.md
+++ b/readme.md
@@ -10,17 +10,9 @@
 
 The Analyser can also be run as part of a Maven build via the [slingfeature-maven-plugin](https://github.com/apache/sling-slingfeature-maven-plugin)
 
-## Running the Analyser from the Commandlien
-
-The analyser can be run from the commandline by running the following main class:
-
-```bash
-java org.apache.sling.feature.analyser.main.Main
-```
-
 # Analyser Tasks
 
-Below is a list of built-in analysers. Additional analysers in relation to Feature Model API Regions can be found here: [org-apache-sling-feature-extension-apiregions](https://github.com/apache/sling-org-apache-sling-feature-extension-apiregions)
+Below is a list of built-in analysers. Additional analysers in relation to Feature Model API Regions can be found in [org-apache-sling-feature-extension-apiregions](https://github.com/apache/sling-org-apache-sling-feature-extension-apiregions), analysers performing checks on class level can be found in [org-apache-sling-feature-analyser-classes](https://github.com/apache/sling-org-apache-sling-feature-analyser-classes).
 
 For further documentation see: [Feature Model](https://github.com/apache/sling-org-apache-sling-feature/blob/master/readme.md)
 
diff --git a/src/main/java/org/apache/sling/feature/analyser/Analyser.java b/src/main/java/org/apache/sling/feature/analyser/Analyser.java
index 32b5cba..7a0dd96 100644
--- a/src/main/java/org/apache/sling/feature/analyser/Analyser.java
+++ b/src/main/java/org/apache/sling/feature/analyser/Analyser.java
@@ -58,6 +58,8 @@
 
     private final Map<String, Map<String, String>> configurations;
 
+    private boolean outputTaskDetails = true;
+
     /**
      * Create new analyser with a provided scanner and the tasks to run
      *
@@ -151,6 +153,15 @@
     }
 
     /**
+     * Enable/disable output of task details. By default the details are outputted.
+     * @param outputTaskDetails flag for enabling/disabling output of task details
+     * @since 1.6.0
+     */
+    public void setOutputTaskDetails(final boolean outputTaskDetails) {
+        this.outputTaskDetails = outputTaskDetails;
+    }
+
+    /**
      * Analyse the feature
      *
      * @param feature The feature to analyse
@@ -186,7 +197,7 @@
             throws Exception {
         logger.info("Starting analyzing feature '{}'...", feature.getId());
 
-        long start = System.currentTimeMillis();
+        final long start = System.currentTimeMillis();
         final FeatureDescriptor featureDesc = scanner.scan(feature);
         BundleDescriptor bd = null;
         ArtifactId framework = fwk;
@@ -200,7 +211,9 @@
         if (framework != null) {
             bd = scanner.scan(framework, feature.getFrameworkProperties());
         }
-        logger.info("- Scanned feature in {}ms", System.currentTimeMillis() - start);
+        if (this.outputTaskDetails) {
+            logger.info("- Scanned feature in {}ms", System.currentTimeMillis() - start);
+        }
         final BundleDescriptor fwkDesc = bd;
 
         final List<AnalyserResult.GlobalReport> globalWarnings = new ArrayList<>();
@@ -217,9 +230,10 @@
 
         // execute analyser tasks
         for (final AnalyserTask task : tasks) {
-
-            logger.info("- Executing {} [{}]...", task.getName(), task.getId());
-            start = System.currentTimeMillis();
+            if (this.outputTaskDetails) {
+                logger.info("- Executing {} [{}]...", task.getName(), task.getId());
+            }
+            final long startTask = System.currentTimeMillis();
             final Map<String, String> taskConfiguration = getConfiguration(task.getId());
 
             task.execute(new AnalyserTaskContext() {
@@ -312,14 +326,15 @@
                     }
                 }
             });
-            logger.info("- Executed {} [{}] in {}ms", task.getName(), task.getId(), System.currentTimeMillis() - start);
+            if (this.outputTaskDetails) {
+                logger.info("- Executed {} [{}] in {}ms", task.getName(), task.getId(), System.currentTimeMillis() - startTask);
+            }
         }
 
-        int allWarnings = globalWarnings.size() + artifactWarnings.size() + extensionWarnings.size() + configurationWarnings.size();
-        int allErrors = globalErrors.size() + artifactErrors.size() + extensionErrors.size()  + configurationErrors.size();
-        logger.info("Analyzing feature '" + feature.getId() + "' finished : "
-                + allWarnings + " warnings, "
-                + allErrors + " errors.");
+        final int allWarnings = globalWarnings.size() + artifactWarnings.size() + extensionWarnings.size() + configurationWarnings.size();
+        final int allErrors = globalErrors.size() + artifactErrors.size() + extensionErrors.size()  + configurationErrors.size();
+        logger.info("Finished analyzing feature '{}' in {}ms : {} warnings, {} errors.",
+            feature.getId(), System.currentTimeMillis() - start, allWarnings, allErrors);
 
         return new AnalyserResult() {
             @Override
diff --git a/src/main/java/org/apache/sling/feature/analyser/extensions/AnalyserMetaDataExtension.java b/src/main/java/org/apache/sling/feature/analyser/extensions/AnalyserMetaDataExtension.java
index 7324772..61c574f 100644
--- a/src/main/java/org/apache/sling/feature/analyser/extensions/AnalyserMetaDataExtension.java
+++ b/src/main/java/org/apache/sling/feature/analyser/extensions/AnalyserMetaDataExtension.java
@@ -22,10 +22,10 @@
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.scanner.BundleDescriptor;
 
-import javax.json.Json;
-import javax.json.JsonObject;
-import javax.json.JsonObjectBuilder;
-import javax.json.JsonValue;
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonValue;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
diff --git a/src/main/java/org/apache/sling/feature/analyser/package-info.java b/src/main/java/org/apache/sling/feature/analyser/package-info.java
index a1f0888..7356714 100644
--- a/src/main/java/org/apache/sling/feature/analyser/package-info.java
+++ b/src/main/java/org/apache/sling/feature/analyser/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@org.osgi.annotation.versioning.Version("1.5.0")
+@org.osgi.annotation.versioning.Version("1.6.0")
 package org.apache.sling.feature.analyser;
 
 
diff --git a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckApisJarsProperties.java b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckApisJarsProperties.java
index 9a01c03..6f0f233 100644
--- a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckApisJarsProperties.java
+++ b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckApisJarsProperties.java
@@ -20,9 +20,9 @@
 import java.net.URL;
 import java.util.Arrays;
 
-import javax.json.JsonObject;
-import javax.json.JsonValue;
-import javax.json.JsonValue.ValueType;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+import jakarta.json.JsonValue.ValueType;
 
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
diff --git a/src/main/java/org/apache/sling/feature/scanner/BundleDescriptor.java b/src/main/java/org/apache/sling/feature/scanner/BundleDescriptor.java
index aad85f4..1333262 100644
--- a/src/main/java/org/apache/sling/feature/scanner/BundleDescriptor.java
+++ b/src/main/java/org/apache/sling/feature/scanner/BundleDescriptor.java
@@ -59,15 +59,6 @@
     public abstract Manifest getManifest();
 
     /**
-     * Get the start level
-     *
-     * @return The start level.
-     * @deprecated Use start order of the bundle
-     */
-    @Deprecated
-    public abstract int getBundleStartLevel();
-
-    /**
      * Is the bundle exporting a package?
      * @param packageName Package name
      * @return {@code true} if that package is exported.
diff --git a/src/main/java/org/apache/sling/feature/scanner/ContentPackageDescriptor.java b/src/main/java/org/apache/sling/feature/scanner/ContentPackageDescriptor.java
index 3157917..afa5554 100644
--- a/src/main/java/org/apache/sling/feature/scanner/ContentPackageDescriptor.java
+++ b/src/main/java/org/apache/sling/feature/scanner/ContentPackageDescriptor.java
@@ -19,7 +19,6 @@
 import java.util.List;
 import java.util.Properties;
 
-import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.Configuration;
 
 /**
diff --git a/src/main/java/org/apache/sling/feature/scanner/Scanner.java b/src/main/java/org/apache/sling/feature/scanner/Scanner.java
index 0f81d40..a55f8bb 100644
--- a/src/main/java/org/apache/sling/feature/scanner/Scanner.java
+++ b/src/main/java/org/apache/sling/feature/scanner/Scanner.java
@@ -121,21 +121,6 @@
         return this.doScan(bundle, bundle.getStartOrder());
     }
 
-    /**
-     * Scan a bundle
-     *
-     * @param bundle     The bundle artifact
-     * @param startLevel The start level of the bundle
-     * @return The bundle descriptor
-     * @throws IOException If something goes wrong or the provided artifact is not a
-     *                     bundle.
-     * @deprecated Use {@link #scanBundle(Artifact)}
-     */
-    @Deprecated
-    public BundleDescriptor scan(final Artifact bundle, final int startLevel) throws IOException {
-        return this.doScan(bundle, startLevel);
-    }
-
     private BundleDescriptor doScan(final Artifact bundle, final int startLevel) throws IOException {
         final String key = bundle.getId().toMvnId().concat(":")
             .concat(String.valueOf(startLevel)).concat(":")
@@ -147,7 +132,7 @@
                 throw new IOException("Unable to find file for " + bundle.getId());
             }
 
-            desc = new BundleDescriptorImpl(bundle, file, startLevel);
+            desc = new BundleDescriptorImpl(bundle, file);
             this.cache.put(key, desc);
         }
         return desc;
@@ -258,7 +243,7 @@
                     if (headers != null) {
                         Manifest manifest = new Manifest();
                         headers.forEach(manifest.getMainAttributes()::putValue);
-                        BundleDescriptor desc = new BundleDescriptorImpl(bundle, artifactProvider, manifest, bundle.getStartOrder());
+                        BundleDescriptor desc = new BundleDescriptorImpl(bundle, artifactProvider, manifest);
                         this.cache.put(key, desc);
                     }
                 }
diff --git a/src/main/java/org/apache/sling/feature/scanner/impl/BundleDescriptorImpl.java b/src/main/java/org/apache/sling/feature/scanner/impl/BundleDescriptorImpl.java
index f91718d..8bd4c06 100644
--- a/src/main/java/org/apache/sling/feature/scanner/impl/BundleDescriptorImpl.java
+++ b/src/main/java/org/apache/sling/feature/scanner/impl/BundleDescriptorImpl.java
@@ -64,9 +64,6 @@
     /** The bundle version. */
     private String bundleVersion;
 
-    /** The start level of this artifact. */
-    private final int startLevel;
-
     /** Manifest */
     private final Manifest manifest;
 
@@ -106,14 +103,12 @@
      * Constructor for a new descriptor
      * @param artifact The artifact
      * @param url The URL
-     * @param startLevel The start level
      * @throws IOException If the manifest can't be get
      * @throws NullPointerException If artifact is {@code null}
      */
     public BundleDescriptorImpl(final Artifact artifact,
-            final URL url,
-            final int startLevel) throws IOException  {
-        this(artifact, url, null, getManifest(url), startLevel);
+            final URL url) throws IOException  {
+        this(artifact, url, null, getManifest(url));
     }
 
     /**
@@ -121,15 +116,13 @@
      * @param artifact The artifact
      * @param provider The artifact provider
      * @param manifest The manifest
-     * @param startLevel The start level
      * @throws IOException If the manifest can't be get
      * @throws NullPointerException If artifact is {@code null}
      */
     public BundleDescriptorImpl(final Artifact artifact,
                                 final ArtifactProvider provider,
-                                final Manifest manifest,
-                                final int startLevel) throws IOException {
-        this(artifact, null, provider, manifest, startLevel);
+                                final Manifest manifest) throws IOException {
+        this(artifact, null, provider, manifest);
     }
 
     /**
@@ -138,18 +131,15 @@
      * @param url The URL
      * @param provider The artifact provider
      * @param manifest The manifest
-     * @param startLevel The start level
      * @throws IOException If the manifest can't be get
      * @throws NullPointerException If artifact is {@code null}
      */
     public BundleDescriptorImpl(final Artifact artifact,
                                 final URL url,
                                 final ArtifactProvider provider,
-                                final Manifest manifest,
-                                final int startLevel) throws IOException  {
+                                final Manifest manifest) throws IOException  {
         super(artifact.getId().toMvnId());
         this.artifact = artifact;
-        this.startLevel = startLevel;
         this.artifactFile = url;
         this.artifactProvider = provider;
         if ( manifest == null ) {
@@ -179,15 +169,6 @@
         return bundleVersion;
     }
 
-    /**
-     * Get the start level
-     * @return The start level or {@code 0} for the default.
-     */
-    @Override
-    public int getBundleStartLevel() {
-        return startLevel;
-    }
-
     @Override
     public URL getArtifactFile() {
         if (artifactFile == null && artifactProvider != null) {
diff --git a/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageScanner.java b/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageScanner.java
index 4b6f0b3..44383d2 100644
--- a/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageScanner.java
+++ b/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageScanner.java
@@ -18,10 +18,8 @@
 
 import java.io.File;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.Reader;
 import java.net.URL;
 import java.nio.file.Files;
 import java.util.ArrayList;
@@ -214,10 +212,9 @@
                                     // ignore
                                 }
 
-                                final Artifact bundle = new Artifact(extractArtifactId(tempDir, newFile));
+                                final Artifact bundle = new Artifact(extractArtifactId(packageArtifact.getId(), newFile));
                                 bundle.setStartOrder(startLevel);
-                                final BundleDescriptor info = new BundleDescriptorImpl(bundle, newFile.toURI().toURL(),
-                                        startLevel);
+                                final BundleDescriptor info = new BundleDescriptorImpl(bundle, newFile.toURI().toURL());
                                 bundle.getMetadata().put(ContentPackageDescriptorImpl.METADATA_PACKAGE,
                                         packageArtifact.getId().toMvnId());
                                 bundle.getMetadata().put(ContentPackageDescriptorImpl.METADATA_PATH, contentPath);
@@ -286,13 +283,10 @@
         }
     }
 
-    private ArtifactId extractArtifactId(final File tempDir, final File bundleFile)
-    throws IOException {
+    final List<Properties> getInitialCandidates(final File bundleFile)  throws IOException{
         logger.debug("Extracting Bundle {}", bundleFile.getName());
 
-        final File toDir = new File(tempDir, bundleFile.getName());
-        toDir.mkdirs();
-
+        final List<Properties> candidates = new ArrayList<>();
         try (final JarFile zipFile = new JarFile(bundleFile)) {
             Enumeration<? extends ZipEntry> entries = zipFile.entries();
 
@@ -302,79 +296,108 @@
                 final String entryName = entry.getName();
                 if ( !entryName.endsWith("/") && entryName.startsWith("META-INF/maven/") && entryName.endsWith("/pom.properties")) {
                     logger.debug("- extracting : {}", entryName);
-                    final File newFile = new File(toDir, entryName.replace('/', File.separatorChar));
-                    newFile.getParentFile().mkdirs();
 
-                    try (
-                            final FileOutputStream fos = new FileOutputStream(newFile);
-                            final InputStream zis = zipFile.getInputStream(entry)
-                    ) {
-                        int len;
-                        while ((len = zis.read(buffer)) > -1) {
-                            fos.write(buffer, 0, len);
-                        }
+                    final Properties props = new Properties();
+                    try (final InputStream zis = zipFile.getInputStream(entry)) {
+                        props.load(zis);
                     }
+                    candidates.add(props);
                 }
             }
         }
+        return candidates;
+    }
 
-        // check for maven
+    private String adjustVersion(final String version) {
+        final String parts[] = version.split("\\.");
+        if ( parts.length == 4 ) {
+            final int lastDot = version.lastIndexOf('.');
+            return version.substring(0, lastDot) + '-' + version.substring(lastDot + 1);
+        }
+        return version;
+    }
 
-        final File metaInfDir = new File(toDir, "META-INF");
-        if ( metaInfDir.exists() ) {
-            final File mavenDir = new File(metaInfDir, "maven");
-            if ( mavenDir.exists() ) {
-                File groupDir = null;
-                for(final File d : mavenDir.listFiles()) {
-                    if ( d.isDirectory() && !d.getName().startsWith(".") ) {
-                        groupDir = d;
-                        break;
-                    }
-                }
-                if ( groupDir != null ) {
-                    File artifactDir = null;
-                    for(final File d : groupDir.listFiles()) {
-                        if ( d.isDirectory() && !d.getName().startsWith(".") ) {
-                            artifactDir = d;
-                            break;
-                        }
-                    }
-                    if ( artifactDir != null ) {
-                        final File propsFile = new File(artifactDir, "pom.properties");
-                        if ( propsFile.exists() ) {
-                            final Properties props = new Properties();
-                            try ( final Reader r = new FileReader(propsFile) ) {
-                                props.load(r);
-                            }
-                            String groupId = props.getProperty("groupId");
-                            String artifactId = props.getProperty("artifactId");
-                            String version = props.getProperty("version");
-                            String classifier = null;
-
-                            // Capture classifier
-                            final int pos = bundleFile.getName().indexOf(version) + version.length();
-                            if ( bundleFile.getName().charAt(pos) == '-') {
-                                classifier = bundleFile.getName().substring(pos + 1, bundleFile.getName().lastIndexOf('.'));
-                            }
-
-                            final String parts[] = version.split("\\.");
-                            if ( parts.length == 4 ) {
-                                final int lastDot = version.lastIndexOf('.');
-                                version = version.substring(0, lastDot) + '-' + version.substring(lastDot + 1);
-                            }
-
-                            if ( groupId != null && artifactId != null && version != null ) {
-                                return new ArtifactId(groupId,
-                                        artifactId,
-                                        version, classifier, null);
-                            }
-                        }
-                    }
-                }
+    private ArtifactId adjustClassifier(ArtifactId id, final String bundleFileName) {
+        // check for classifier
+        final int versionStart = bundleFileName.indexOf(id.getVersion());
+        if ( versionStart != -1 ) {
+            // capture classifier
+            final int versionEnd = versionStart + id.getVersion().length();
+            if ( bundleFileName.length() > versionEnd && bundleFileName.charAt(versionEnd) == '-') {
+                id = id.changeClassifier(bundleFileName.substring(versionEnd + 1, bundleFileName.lastIndexOf('.')));
             }
         }
+        return id;
+    }
 
-        throw new IOException(bundleFile.getName() + " has no maven coordinates!");
+    private ArtifactId extractArtifactId(final ArtifactId packageArtifactId, final File bundleFile)
+    throws IOException {
+        final List<Properties> candidates = this.getInitialCandidates(bundleFile);
+        return extractArtifactId(candidates, bundleFile.getName(), packageArtifactId);
+    }
+
+    private List<ArtifactId> getArtifactIds(final List<Properties> candidates) {
+        final List<ArtifactId> idCandidates = new ArrayList<>();
+        for(final Properties props : candidates) {
+            final String version = props.getProperty("version");
+            final String groupId = props.getProperty("groupId");
+            final String artifactId = props.getProperty("artifactId");
+
+            if ( version != null && groupId != null && artifactId != null ) {
+                idCandidates.add(new ArtifactId(groupId, artifactId, adjustVersion(version), null, null));
+            }
+        }
+        return idCandidates;
+    }
+
+    private List<ArtifactId> filterCandidatesByVersion(final List<ArtifactId> ids, final String bundleFileName) {
+        final List<ArtifactId> idCandidates = new ArrayList<>();
+        for(final ArtifactId id : ids) {
+            final int versionStart = bundleFileName.indexOf(id.getVersion());
+            if ( versionStart != -1 ) {
+                idCandidates.add(id);
+            }
+        }
+        return idCandidates;
+    }
+
+    ArtifactId extractArtifactId(final List<Properties> candidates, final String bundleFileName, final ArtifactId packageArtifactId)
+    throws IOException {
+        logger.debug("Properties candidates for {} : {}", bundleFileName, candidates);
+
+        final List<ArtifactId> idCandidates = getArtifactIds(candidates);
+        logger.debug("Artifact candidates for {} : {}", bundleFileName, candidates);
+
+        // single candidate? return
+        if ( idCandidates.size() == 1 ) {
+            final ArtifactId result = adjustClassifier(idCandidates.get(0), bundleFileName);
+            logger.debug("Found single candidate : {}", result);
+            return result;
+        }
+
+        // more than one candidate, find matching version
+        final List<ArtifactId> versionIds = filterCandidatesByVersion(idCandidates, bundleFileName);
+        if ( versionIds.size() == 1 ) {
+            final ArtifactId result = adjustClassifier(versionIds.get(0), bundleFileName);
+            logger.debug("Found single candidate matching version : {}", result);
+            return result;
+        }
+        // check parent group id
+        for(final ArtifactId id : versionIds.isEmpty() ? idCandidates : versionIds) {
+            if ( id.getGroupId().equals(packageArtifactId.getGroupId()) ) {
+                final ArtifactId result = adjustClassifier(id, bundleFileName);
+                logger.debug("Found candidate with parent group id {} : {}",packageArtifactId.getGroupId(), result);
+                return result;
+            }
+        }
+        // randomly pick one
+        if ( idCandidates.size() > 0 ) {
+            final ArtifactId result = adjustClassifier(idCandidates.get(0), bundleFileName);
+            logger.debug("Picking random candidate : {}", result);
+            return result;
+        }
+
+        throw new IOException(bundleFileName + " has no maven coordinates!");
     }
 
     Configuration processConfiguration(final File configFile,
diff --git a/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java b/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java
index 8c3d85c..c7db4b9 100644
--- a/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java
+++ b/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java
@@ -86,11 +86,6 @@
             }
 
             @Override
-            public int getBundleStartLevel() {
-                return 0;
-            }
-
-            @Override
             public URL getArtifactFile() {
                 return platformFile;
             }
diff --git a/src/main/java/org/apache/sling/feature/scanner/package-info.java b/src/main/java/org/apache/sling/feature/scanner/package-info.java
index c0f3a77..8fdbfc7 100644
--- a/src/main/java/org/apache/sling/feature/scanner/package-info.java
+++ b/src/main/java/org/apache/sling/feature/scanner/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@org.osgi.annotation.versioning.Version("2.3.0")
+@org.osgi.annotation.versioning.Version("3.0.0")
 package org.apache.sling.feature.scanner;
 
 
diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImportsTest.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImportsTest.java
index ad2e4a5..dc16885 100644
--- a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImportsTest.java
+++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImportsTest.java
@@ -159,7 +159,7 @@
 
     private void fdAddBundle(FeatureDescriptor fd, String id, String file) throws IOException {
         BundleDescriptor bd1 = new BundleDescriptorImpl(
-                new Artifact(ArtifactId.fromMvnId(id)), new File(resourceRoot, file).toURI().toURL(), 0);
+                new Artifact(ArtifactId.fromMvnId(id)), new File(resourceRoot, file).toURI().toURL());
         fd.getBundleDescriptors().add(bd1);
     }
 }
diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleUnversionedPackagesTest.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleUnversionedPackagesTest.java
index 3b80692..0757b6c 100644
--- a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleUnversionedPackagesTest.java
+++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleUnversionedPackagesTest.java
@@ -81,7 +81,7 @@
         Artifact artifact = new Artifact(ArtifactId.fromMvnId(id));
         artifact.setFeatureOrigins(origins);
         BundleDescriptor bd1 = new BundleDescriptorImpl(
-                artifact, new File(resourceRoot, file).toURI().toURL(), 0);
+                artifact, new File(resourceRoot, file).toURI().toURL());
         fd.getBundleDescriptors().add(bd1);
     }
 }
diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesTest.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesTest.java
index 01603bb..8df0c50 100644
--- a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesTest.java
+++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesTest.java
@@ -19,6 +19,7 @@
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import java.io.File;
 import java.util.List;
 import java.util.Properties;
 
@@ -67,7 +68,7 @@
         analyser.execute(ctx);
         List<String> errors = ctx.getErrors();
         assertThat(errors.size(), equalTo(1));
-        assertThat(errors.get(0), equalTo("ValidationViolation: \"jackrabbit-docviewparser: Invalid XML found: The reference to entity \"se\" must end with the ';' delimiter.\", filePath=jcr_root/apps/cschneidervalidation/configs/com.adobe.test.Invalid.xml, nodePath=/apps/cschneidervalidation/configs/com.adobe.test.Invalid"));
+        assertThat(errors.get(0), equalTo(String.format("ValidationViolation: \"jackrabbit-docviewparser: Invalid XML found: The reference to entity \"se\" must end with the ';' delimiter.\", filePath=%s, nodePath=/apps/cschneidervalidation/configs/com.adobe.test.Invalid", "jcr_root/apps/cschneidervalidation/configs/com.adobe.test.Invalid.xml".replace('/', File.separatorChar))));
     }
     
     @Test
diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilitiesTest.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilitiesTest.java
index b08e095..2142c64 100644
--- a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilitiesTest.java
+++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilitiesTest.java
@@ -45,7 +45,7 @@
 
         BundleDescriptor bd1 = new BundleDescriptorImpl(
                 new Artifact(ArtifactId.fromMvnId("g:b1:1.2.0")),
-                f.toURI().toURL(), 7);
+                f.toURI().toURL());
 
         Feature feature = new Feature(ArtifactId.fromMvnId("a:b:1"));
 
@@ -80,7 +80,7 @@
 
         BundleDescriptor bd1 = new BundleDescriptorImpl(
                 new Artifact(ArtifactId.fromMvnId("g:b1:1.2.0")),
-                f.toURI().toURL(), 7);
+                f.toURI().toURL());
 
         Feature feature = new Feature(ArtifactId.fromMvnId("a:b:1"));
         FeatureDescriptor fd = new FeatureDescriptorImpl(feature);
diff --git a/src/test/java/org/apache/sling/feature/scanner/impl/BundleDescriptorImplTest.java b/src/test/java/org/apache/sling/feature/scanner/impl/BundleDescriptorImplTest.java
index 69c4279..1fa26b7 100644
--- a/src/test/java/org/apache/sling/feature/scanner/impl/BundleDescriptorImplTest.java
+++ b/src/test/java/org/apache/sling/feature/scanner/impl/BundleDescriptorImplTest.java
@@ -63,7 +63,7 @@
             + "Bundle-ManifestVersion: 2\n"
             + "Export-Package: org.apache.sling;version=1.0,org.apache.felix;version=2.0\n";
         URL f = new URL("jar:" + createBundle(bmf).toURI().toURL() + "!/");
-        BundleDescriptorImpl bdf = new BundleDescriptorImpl(new Artifact(new ArtifactId("foo", "bar", "1.0", "bla", "bundle")), f, 1);
+        BundleDescriptorImpl bdf = new BundleDescriptorImpl(new Artifact(new ArtifactId("foo", "bar", "1.0", "bla", "bundle")), f);
         final Set<PackageInfo> infos = bdf.getExportedPackages();
         assertEquals(2, infos.size());
         assertPackageInfo(infos ,"org.apache.sling", Version.parseVersion("1.0"));
@@ -94,7 +94,7 @@
         File dir = createBundleFolder(bmf);
         try {
             URL f = dir.toURI().toURL();
-            BundleDescriptorImpl bdf = new BundleDescriptorImpl(new Artifact(new ArtifactId("foo", "bar", "1.0", "bla", "bundle")), f, 1);
+            BundleDescriptorImpl bdf = new BundleDescriptorImpl(new Artifact(new ArtifactId("foo", "bar", "1.0", "bla", "bundle")), f);
             final Set<PackageInfo> infos = bdf.getExportedPackages();
             assertEquals(2, infos.size());
             assertPackageInfo(infos ,"org.apache.sling", Version.parseVersion("1.0"));
diff --git a/src/test/java/org/apache/sling/feature/scanner/impl/ContentPackageScannerTest.java b/src/test/java/org/apache/sling/feature/scanner/impl/ContentPackageScannerTest.java
index 6a103ee..dcbc26d 100644
--- a/src/test/java/org/apache/sling/feature/scanner/impl/ContentPackageScannerTest.java
+++ b/src/test/java/org/apache/sling/feature/scanner/impl/ContentPackageScannerTest.java
@@ -26,7 +26,10 @@
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
 import java.util.Set;
 
 import org.apache.sling.feature.Artifact;
@@ -56,12 +59,79 @@
             assertNotNull(desc.getManifest());
         }
     }
+    
+    @Test
+    public void testMultipleMavenPropertyDirectoryPicking() throws URISyntaxException, IOException {
+        // this test case is to cover where we have multiple pom.properties files present in our package.
+        final File file = getTestFile("/test-content-felix-bundle-multi-maven-properties.zip");
+
+        final String COORDINATES_TEST_PACKAGE_A_10 = "org.apache.felix:org.apache.felix.framework:6.0.1";
+        final ArtifactId TEST_PACKAGE_AID_A_10 = ArtifactId.fromMvnId(COORDINATES_TEST_PACKAGE_A_10);
+
+        ContentPackageScanner scanner = new ContentPackageScanner();
+        Set<ContentPackageDescriptorImpl> descriptors = scanner.scan(new Artifact(TEST_PACKAGE_AID_A_10), file.toURI().toURL());
+        
+        for(ContentPackageDescriptorImpl descriptor: descriptors){
+            if(descriptor.getName().equals("test-content-felix-bundle-multi-maven-properties")){
+                assertEquals("org.apache.felix:org.apache.felix.framework:6.0.1", descriptor.getBundles().get(0).getName());
+            }
+       
+        }
+    }
+
+    @Test
+    public void testMultipleMavenPropertiesMatchingParent() throws IOException {
+        final ContentPackageScanner scanner = new ContentPackageScanner();
+        final List<Properties> candidates = new ArrayList<>();
+        final Properties guava = new Properties();
+        guava.put("groupId", "com.google.guava");
+        guava.put("artifactId", "failureaccess");
+        guava.put("version", "14.0");
+        candidates.add(guava);
+        final Properties acs = new Properties();
+        acs.put("groupId", "com.adobe.acs");
+        acs.put("artifactId", "acs-aem-commons-bundle");
+        acs.put("version", "5.3.7");
+        candidates.add(acs);
+        final ArtifactId id = scanner.extractArtifactId(candidates, "acs-aem-commons-bundle-5.3.4.jar", ArtifactId.parse("com.adobe.acs:acs-aem-commons-content:zip:5.4.3.zip"));        // xample we are extracting com.acs.aem.acs-aem-commons-content-5.4.3.zip > acs-aem-commons-bundle-5.3.4.jar
+        assertEquals(ArtifactId.parse("com.adobe.acs:acs-aem-commons-bundle:5.3.7"), id);
+    }
+
+    @Test
+    public void testMavenPropertiesClassifier() throws IOException {
+        final ContentPackageScanner scanner = new ContentPackageScanner();
+        final List<Properties> candidates = new ArrayList<>();
+        final Properties acs = new Properties();
+        acs.put("groupId", "com.adobe.acs");
+        acs.put("artifactId", "acs-aem-commons-bundle");
+        acs.put("version", "5.3.4");
+        candidates.add(acs);
+        final ArtifactId id = scanner.extractArtifactId(candidates, "acs-aem-commons-bundle-5.3.4-test.jar", ArtifactId.parse("com.adobe.acs:acs-aem-commons-content:zip:5.4.3.zip"));        // xample we are extracting com.acs.aem.acs-aem-commons-content-5.4.3.zip > acs-aem-commons-bundle-5.3.4.jar
+        assertEquals(ArtifactId.parse("com.adobe.acs:acs-aem-commons-bundle:jar:test:5.3.4"), id);
+    }
+
+    @Test
+    public void testMultipleMavenPropertiesMatchingVersion() throws IOException {
+        final ContentPackageScanner scanner = new ContentPackageScanner();
+        final List<Properties> candidates = new ArrayList<>();
+        final Properties guava = new Properties();
+        guava.put("groupId", "com.google.guava");
+        guava.put("artifactId", "failureaccess");
+        guava.put("version", "14.0");
+        candidates.add(guava);
+        final Properties acs = new Properties();
+        acs.put("groupId", "com.adobe.acs");
+        acs.put("artifactId", "acs-aem-commons-bundle");
+        acs.put("version", "5.3.4");
+        candidates.add(acs);
+        final ArtifactId id = scanner.extractArtifactId(candidates, "acs-aem-commons-bundle-5.3.4.jar", ArtifactId.parse("something:acs-aem-commons-content:zip:5.4.3.zip"));        // xample we are extracting com.acs.aem.acs-aem-commons-content-5.4.3.zip > acs-aem-commons-bundle-5.3.4.jar
+        assertEquals(ArtifactId.parse("com.adobe.acs:acs-aem-commons-bundle:5.3.4"), id);
+    }
 
     private File getTestFile(String path) throws URISyntaxException {
         return new File(getClass().getResource(path).toURI());
     }
 
-    @SuppressWarnings("deprecation")
     private void assetDescriptor(ContentPackageDescriptorImpl desc, String descName, final ArtifactId descArtifactId, final URL descUrl) {
         assertEquals(descName, desc.getName());
         assertEquals(descArtifactId, desc.getArtifact().getId());
@@ -69,9 +139,8 @@
 
         assertEquals(1, desc.getBundles().size());
 
-        assertEquals(desc.getBundles().get(0).getArtifact().getId().toString(), "org.apache.felix:org.apache.felix.framework:jar:bundle:6.0.1");
+        assertEquals(desc.getBundles().get(0).getArtifact().getId(), ArtifactId.parse("org.apache.felix:org.apache.felix.framework:6.0.1"));
         assertEquals("artifact start order",20, desc.getBundles().get(0).getArtifact().getStartOrder());
-        assertEquals("bundle start level",20, desc.getBundles().get(0).getBundleStartLevel());
 
         assertEquals(1, desc.getConfigurations().size());
         assertConfiguration(desc.getConfigurations().get(0));
diff --git a/src/test/resources/test-content-felix-bundle-multi-maven-properties.zip b/src/test/resources/test-content-felix-bundle-multi-maven-properties.zip
new file mode 100644
index 0000000..053b2ab
--- /dev/null
+++ b/src/test/resources/test-content-felix-bundle-multi-maven-properties.zip
Binary files differ