@@ -4422,6 +4422,75 @@ async function loadReconUnifiedTable(scanId, allFiles, containerId, scanRecord)
44224422
44234423 allRows = allRows . map ( rowToGrid ) ;
44244424
4425+ const extractApkPackageInfo = ( rows ) => {
4426+ const info = { } ;
4427+ const consumed = new Set ( ) ;
4428+ const aliases = {
4429+ package_name : 'package_name' ,
4430+ package : 'package_name' ,
4431+ packageid : 'package_name' ,
4432+ package_id : 'package_name' ,
4433+ applicationid : 'package_name' ,
4434+ application_id : 'package_name' ,
4435+ appid : 'package_name' ,
4436+ app_name : 'app_name' ,
4437+ appname : 'app_name' ,
4438+ name : 'app_name' ,
4439+ version : 'version' ,
4440+ version_name : 'version' ,
4441+ versionname : 'version' ,
4442+ version_code : 'version_code' ,
4443+ versioncode : 'version_code' ,
4444+ min_sdk : 'min_sdk' ,
4445+ minsdk : 'min_sdk' ,
4446+ target_sdk : 'target_sdk' ,
4447+ targetsdk : 'target_sdk' ,
4448+ compile_sdk : 'compile_sdk' ,
4449+ compilesdk : 'compile_sdk' ,
4450+ } ;
4451+ const takeKV = ( k , v ) => {
4452+ const key = String ( k || '' ) . trim ( ) . toLowerCase ( ) . replace ( / [ ^ a - z 0 - 9 _ ] / g, '' ) ;
4453+ const mapped = aliases [ key ] ;
4454+ if ( ! mapped ) return ;
4455+ const val = String ( v ?? '' ) . trim ( ) ;
4456+ if ( ! val ) return ;
4457+ if ( ! info [ mapped ] ) info [ mapped ] = val ;
4458+ } ;
4459+
4460+ rows . forEach ( ( r , idx ) => {
4461+ const target = String ( r . target || '' ) ;
4462+ const finding = String ( r . finding || '' ) . trim ( ) ;
4463+ if ( ! / [ { ] / . test ( finding ) && ! / ( p a c k a g e | v e r s i o n | s d k | a p p _ n a m e | a p p l i c a t i o n _ i d ) / i. test ( `${ target } ${ finding } ` ) ) return ;
4464+
4465+ let consumedRow = false ;
4466+ try {
4467+ const parsed = JSON . parse ( finding ) ;
4468+ if ( parsed && typeof parsed === 'object' && ! Array . isArray ( parsed ) ) {
4469+ Object . entries ( parsed ) . forEach ( ( [ k , v ] ) => takeKV ( k , v ) ) ;
4470+ consumedRow = Object . keys ( parsed ) . length > 0 ;
4471+ }
4472+ } catch ( _ ) {
4473+ // ignore
4474+ }
4475+ const kvs = finding . match ( / " ? ( [ A - Z a - z _ ] [ A - Z a - z 0 - 9 _ ] * ) " ? \s * : \s * " ? ( [ ^ , " } ] + ) " ? / g) || [ ] ;
4476+ kvs . forEach ( ( frag ) => {
4477+ const m = frag . match ( / " ? ( [ A - Z a - z _ ] [ A - Z a - z 0 - 9 _ ] * ) " ? \s * : \s * " ? ( [ ^ , " } ] + ) " ? / ) ;
4478+ if ( m ) takeKV ( m [ 1 ] , m [ 2 ] ) ;
4479+ } ) ;
4480+ if ( kvs . length ) consumedRow = true ;
4481+ if ( consumedRow ) consumed . add ( idx ) ;
4482+ } ) ;
4483+
4484+ return { info, rows : rows . filter ( ( _ , idx ) => ! consumed . has ( idx ) ) } ;
4485+ } ;
4486+
4487+ let apkPackageInfo = null ;
4488+ if ( isAPKScan ) {
4489+ const extracted = extractApkPackageInfo ( allRows ) ;
4490+ allRows = extracted . rows ;
4491+ apkPackageInfo = extracted . info ;
4492+ }
4493+
44254494 const apkCategoryKey = ( r ) => {
44264495 const t = String ( r . target || '' ) . trim ( ) ;
44274496 if ( t && t !== '-' && t !== '—' ) return t ;
@@ -4497,6 +4566,7 @@ async function loadReconUnifiedTable(scanId, allFiles, containerId, scanRecord)
44974566 <div id="recon-left-rail" style="display:flex;flex-direction:column;gap:6px;padding:8px;overflow-y:auto;overflow-x:hidden;max-height:780px;scrollbar-width:thin"></div>
44984567 </aside>
44994568 <section style="min-width:0">
4569+ <div id="recon-apk-meta" style="display:none;padding:10px 12px;border-bottom:1px solid var(--border);background:rgba(34,211,238,.06)"></div>
45004570 <div id="recon-filter-bar" style="display:grid;grid-template-columns:minmax(200px,1.5fr) 140px 140px minmax(180px,1fr) auto;gap:8px;padding:10px;border-bottom:1px solid var(--border);background:rgba(2,6,23,.5)">
45014571 <input id="recon-filter-host" type="search" placeholder="🔍 Filter by target URL..." style="padding:8px 10px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary);font-size:12px"/>
45024572 <select id="recon-filter-severity" style="padding:8px 10px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text-primary);font-size:12px">
@@ -4550,11 +4620,43 @@ async function loadReconUnifiedTable(scanId, allFiles, containerId, scanRecord)
45504620 </div>` ;
45514621
45524622 const tabsEl = root . querySelector ( '#recon-left-rail' ) ;
4623+ const apkMetaBar = root . querySelector ( '#recon-apk-meta' ) ;
45534624 const filterBar = root . querySelector ( '#recon-filter-bar' ) ;
45544625 const standardView = root . querySelector ( '#recon-standard-view' ) ;
45554626 const assetsView = root . querySelector ( '#recon-assets-view' ) ;
45564627 const assetsContent = root . querySelector ( '#recon-assets-content' ) ;
45574628
4629+ if ( apkMetaBar ) {
4630+ if ( isAPKScan && apkPackageInfo ) {
4631+ const ordered = [
4632+ [ 'package_name' , 'Package ID' ] ,
4633+ [ 'app_name' , 'App Name' ] ,
4634+ [ 'version' , 'Version' ] ,
4635+ [ 'version_code' , 'Version Code' ] ,
4636+ [ 'min_sdk' , 'Min SDK' ] ,
4637+ [ 'target_sdk' , 'Target SDK' ] ,
4638+ [ 'compile_sdk' , 'Compile SDK' ] ,
4639+ ] . filter ( ( [ k ] ) => apkPackageInfo [ k ] ) ;
4640+ if ( ordered . length ) {
4641+ apkMetaBar . style . display = 'flex' ;
4642+ apkMetaBar . style . flexWrap = 'wrap' ;
4643+ apkMetaBar . style . gap = '8px' ;
4644+ apkMetaBar . innerHTML = ordered . map ( ( [ k , label ] ) => (
4645+ `<div style="display:flex;align-items:center;gap:6px;padding:6px 10px;border:1px solid rgba(34,211,238,.28);border-radius:8px;background:rgba(2,6,23,.45)">
4646+ <span style="font-size:11px;color:#67e8f9">${ esc ( label ) } :</span>
4647+ <span style="font-size:11px;color:var(--text-primary);font-family:var(--font-mono,monospace)">${ esc ( apkPackageInfo [ k ] ) } </span>
4648+ </div>`
4649+ ) ) . join ( '' ) ;
4650+ } else {
4651+ apkMetaBar . style . display = 'none' ;
4652+ apkMetaBar . innerHTML = '' ;
4653+ }
4654+ } else {
4655+ apkMetaBar . style . display = 'none' ;
4656+ apkMetaBar . innerHTML = '' ;
4657+ }
4658+ }
4659+
45584660 const modSelect = root . querySelector ( '#recon-filter-module' ) ;
45594661 if ( modSelect ) {
45604662 const usedModules = [ ...new Set ( allRows . map ( r => normalizeModuleKey ( r . module ) ) ) ]
0 commit comments