@@ -974,3 +974,181 @@ def get_command_audit(self, id, metrics=[]):
974974 metrics = "&metrics=" + json .dumps (metrics ) if metrics else "" )
975975 res = requests .get (url , headers = self .hdrs , verify = self .ssl_verify )
976976 return self ._request_result (res )
977+
978+ def list_image_profiles (self ):
979+ '''**Description**
980+ List the current set of image profiles.
981+
982+ **Arguments**
983+ - None
984+
985+ **Success Return Value**
986+ A JSON object containing the details of each profile.
987+
988+ '''
989+ url = "{url}/api/profiling/v1/secure/profileGroups/0/profiles" .format (
990+ url = self .url
991+ )
992+
993+ res = requests .get (url , headers = self .hdrs , verify = self .ssl_verify )
994+ return self ._request_result (res )
995+
996+
997+ def get_image_profile (self , profileId ):
998+ '''**Description**
999+ Find the image profile with a (partial) profile ID <profileId> and return its json description.
1000+
1001+ **Arguments**
1002+ - name: the name of the image profile to fetch
1003+
1004+ **Success Return Value**
1005+ A JSON object containing the description of the image profile. If there is no image profile with
1006+ the given name, returns False. Moreover, it could happen that more than one profile IDs have a collision.
1007+ It is due to the fact that a partial profile ID can be passed and interpreted; in this case a set of
1008+ collision profiles is returned, and the full complete ID string is printed. In this case, it returns
1009+ false.
1010+
1011+ '''
1012+
1013+ # RETRIEVE ALL THE IMAGE PROFILES
1014+ ok , image_profiles = self .list_image_profiles ()
1015+
1016+ if not ok :
1017+ return [False , self .lasterr ]
1018+
1019+
1020+ '''
1021+ The content of the json stored in the image_profiles dictionary:
1022+
1023+ {
1024+ "offset": 0,
1025+ "limit": 99,
1026+ "canLoadMore": false,
1027+ "profiles": [
1028+ ...
1029+ ]
1030+ }
1031+ '''
1032+
1033+ matched_profiles = self .__get_matched_profileIDs (profileId , image_profiles ['profiles' ])
1034+
1035+ # Profile ID not found
1036+ if len (matched_profiles ) == 0 :
1037+ return [False , "No profile with ID {}" .format (profileId )]
1038+
1039+ # Principal workflow. Profile ID found
1040+ elif len (matched_profiles ) == 1 :
1041+ # Matched id. Return information
1042+ url = "{url}/api/profiling/v1/secure/profiles/{profileId}" .format (
1043+ url = self .url ,
1044+ profileId = matched_profiles [0 ]['profileId' ]
1045+ )
1046+
1047+ res = requests .get (url , headers = self .hdrs , verify = self .ssl_verify )
1048+ return self ._request_result (res )
1049+
1050+ # Collision detected. The full profile IDs are returned
1051+ elif len (matched_profiles ) >= 2 :
1052+ return [False , matched_profiles ]
1053+
1054+
1055+ def __get_matched_profileIDs (self , requested_profile , profile_list ):
1056+ '''
1057+ **Description**
1058+ Helper function for retrieving the list of matching profile
1059+
1060+ **Arguments**
1061+ - the requested profile Id (string)
1062+ - List of dictionary, where each dictionary contains the profile information
1063+
1064+ **Success Return Value**
1065+ List of dictionary, where each dictionary represents a profile with the ID prefix substring
1066+ matching the requested one
1067+
1068+ **Content structure of the profile_list parameter**
1069+ This array of profiles contains all the relevant information. For the purposes of this function, only
1070+ the profileId field is relevant.
1071+
1072+ [
1073+ {
1074+ "profileGroupId": 0,
1075+ "profileId": "00000000000000000000000000000000000000000000",
1076+ "profileVersion": 0,
1077+ "profileName": "AAA/BBB:XYZ@0000000000000000000000",
1078+ "imageId": "00000000000000000000000000000000000000000000",
1079+ "imageName": "AAA/BBB:XYZ",
1080+ "processesProposal": {
1081+ "subcategories": [
1082+ {
1083+ "name": "process",
1084+ "ruleName": "process - 00000000000000000000000000000000000000000000",
1085+ "ruleType": "PROCESS",
1086+ "score": 000
1087+ }
1088+ ],
1089+ "score": 000
1090+ },
1091+ "fileSystemProposal": {
1092+ "subcategories": [
1093+ {
1094+ "name": "filesystem",
1095+ "ruleName": "filesystem - 00000000000000000000000000000000000000000000",
1096+ "ruleType": "FILESYSTEM",
1097+ "score": 000
1098+ }
1099+ ],
1100+ "score": 000
1101+ },
1102+ "syscallProposal": {
1103+ "subcategories": [
1104+ {
1105+ "name": "syscalls",
1106+ "ruleName": "syscalls - 00000000000000000000000000000000000000000000",
1107+ "ruleType": "SYSCALL",
1108+ "score": 000
1109+ }
1110+ ],
1111+ "score": 000
1112+ },
1113+ "networkProposal": {
1114+ "subcategories": [
1115+ {
1116+ "name": "network",
1117+ "ruleName": "network - 00000000000000000000000000000000000000000000",
1118+ "ruleType": "NETWORK",
1119+ "score": 000
1120+ }
1121+ ],
1122+ "score": 000
1123+ },
1124+ "containerImagesProposal": {
1125+ "subcategories": [
1126+ {
1127+ "name": "container image",
1128+ "ruleName": "container image - 00000000000000000000000000000000000000000000",
1129+ "ruleType": "CONTAINER",
1130+ "score": 0
1131+ }
1132+ ],
1133+ "score": 0
1134+ },
1135+ "status": "STATUS_VALUE",
1136+ "score": 000
1137+ },
1138+ ...
1139+ ]
1140+ '''
1141+
1142+ matched_profiles = []
1143+
1144+ request_len = len (requested_profile )
1145+ for profile in profile_list :
1146+
1147+ # get the length of the substring to match
1148+ str_len_match = min (len (profile ), request_len )
1149+
1150+ if profile ['profileId' ][0 :str_len_match ] == requested_profile [0 :str_len_match ]:
1151+ matched_profiles .append (profile )
1152+
1153+ return matched_profiles
1154+
0 commit comments